diff --git a/apps/server/integration/transferBudgetV2.integration.test.ts b/apps/server/integration/transferBudgetV2.integration.test.ts index bfeb7868a80a..c992a923d9c0 100644 --- a/apps/server/integration/transferBudgetV2.integration.test.ts +++ b/apps/server/integration/transferBudgetV2.integration.test.ts @@ -40,10 +40,10 @@ import { OrchestrationEventStoreLive } from "../src/persistence/Layers/Orchestra import * as EventStore from "../src/orchestration-v2/EventStore.ts"; import * as EventSink from "../src/orchestration-v2/EventSink.ts"; import * as ProjectionStore from "../src/orchestration-v2/ProjectionStore.ts"; -import { ThreadManagementService } from "../src/orchestration-v2/ThreadManagementService.ts"; +import * as ThreadManagementService from "../src/orchestration-v2/ThreadManagementService.ts"; import * as ProjectStore from "../src/orchestration-v2/ProjectStore.ts"; -import { ProjectService } from "../src/project/ProjectService.ts"; -import { ProjectEnrichmentService } from "../src/project/ProjectEnrichmentService.ts"; +import * as ProjectService from "../src/project/ProjectService.ts"; +import * as ProjectEnrichmentService from "../src/project/ProjectEnrichmentService.ts"; import { orchestrationHttpApiLayer } from "../src/orchestration-v2/http.ts"; import { httpCompressionLayer } from "../src/http.ts"; import { subscribeOrchestrationV2Thread, subscribeOrchestrationV2Shell } from "../src/ws.ts"; @@ -81,7 +81,7 @@ const management = Layer.unwrap( Effect.gen(function* () { const projections = yield* ProjectionStore.ProjectionStoreV2; const sink = yield* EventSink.EventSinkV2; - return Layer.mock(ThreadManagementService)({ + return Layer.mock(ThreadManagementService.ThreadManagementService)({ ensureLegacyTranscript: () => Effect.void, getThreadSnapshot: (id) => projections.getThreadSnapshot(id).pipe(Effect.orDie), getThreadSnapshotWindow: (id, options) => @@ -96,7 +96,7 @@ const management = Layer.unwrap( const enrichment = Layer.unwrap( Effect.gen(function* () { const changes = yield* PubSub.unbounded(); - return Layer.mock(ProjectEnrichmentService)({ + return Layer.mock(ProjectEnrichmentService.ProjectEnrichmentService)({ getAvailable: () => Effect.succeed({ repositoryIdentity: null, @@ -110,7 +110,7 @@ const enrichment = Layer.unwrap( // The transfer history has no project events, so shell streams never read a project shell. const services = management.pipe( Layer.provideMerge(ProjectStore.layer), - Layer.provideMerge(Layer.mock(ProjectService)({})), + Layer.provideMerge(Layer.mock(ProjectService.ProjectService)({})), Layer.provideMerge(enrichment), Layer.provideMerge(persistence), ); diff --git a/apps/server/scripts/record-grok-acp-replay-fixture.ts b/apps/server/scripts/record-grok-acp-replay-fixture.ts index 32274f5cf5e5..f09a3e51f0cb 100644 --- a/apps/server/scripts/record-grok-acp-replay-fixture.ts +++ b/apps/server/scripts/record-grok-acp-replay-fixture.ts @@ -24,7 +24,7 @@ import * as Schema from "effect/Schema"; import { ChildProcessSpawner } from "effect/unstable/process"; import type * as EffectAcpProtocol from "effect-acp/protocol"; -import { ServerConfig } from "../src/config.ts"; +import * as ServerConfig from "../src/config.ts"; import { GROK_DEFAULT_INSTANCE_ID, GROK_PROVIDER, @@ -457,7 +457,7 @@ const recordScenario = Effect.fn("recordGrokScenario")(function* (fixtureName: s crypto: yield* Crypto.Crypto, fileSystem: yield* FileSystem.FileSystem, idAllocator: yield* IdAllocator.IdAllocatorV2, - serverConfig: yield* ServerConfig, + serverConfig: yield* ServerConfig.ServerConfig, selfInvocation: yield* resolveSelfInvocation(), continuationRequests: yield* ProviderContinuationRequests.ProviderContinuationRequests, // Production's runtime factory, with the protocol logger teeing raw lines. @@ -498,9 +498,10 @@ const recordScenario = Effect.fn("recordGrokScenario")(function* (fixtureName: s ).pipe( Layer.provide( Layer.mergeAll( - Layer.effect(ServerConfig, makeReplayServerConfig(`grok-record-${fixtureName}`)).pipe( - Layer.provide(NodeServices.layer), - ), + Layer.effect( + ServerConfig.ServerConfig, + makeReplayServerConfig(`grok-record-${fixtureName}`), + ).pipe(Layer.provide(NodeServices.layer)), NodeServices.layer, IdAllocator.layer, ), diff --git a/apps/server/src/auth/EnvironmentAuth.ts b/apps/server/src/auth/EnvironmentAuth.ts index 481ffc2fad64..4254691a2487 100644 --- a/apps/server/src/auth/EnvironmentAuth.ts +++ b/apps/server/src/auth/EnvironmentAuth.ts @@ -38,7 +38,7 @@ import * as ServerSecretStore from "./ServerSecretStore.ts"; import * as SessionStore from "./SessionStore.ts"; import { REUSABLE_DEV_SESSION_EXPIRES_AT, resolveReusableDevAuth } from "./ReusableDevAuth.ts"; import { verifyRequestDpopProof } from "./dpop.ts"; -import { layerConfig as SqlitePersistenceLayer } from "../persistence/Layers/Sqlite.ts"; +import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; const DEFAULT_SESSION_SUBJECT = "cli-issued-session"; export const INTERNAL_ADMINISTRATIVE_BOOTSTRAP_SUBJECT = "administrative-bootstrap"; @@ -1125,7 +1125,7 @@ export const layer = Layer.effect(EnvironmentAuth, make).pipe( Layer.provideMerge(EnvironmentAuthPolicy.layer), ); -const storageLayer = Layer.mergeAll(ServerSecretStore.layer, SqlitePersistenceLayer); +const storageLayer = Layer.mergeAll(ServerSecretStore.layer, SqlitePersistence.layerConfig); export const runtimeLayer = layer.pipe( Layer.provideMerge(storageLayer), diff --git a/apps/server/src/background/BackgroundPolicy.test.ts b/apps/server/src/background/BackgroundPolicy.test.ts index 441a518d2c89..7f3e76e34020 100644 --- a/apps/server/src/background/BackgroundPolicy.test.ts +++ b/apps/server/src/background/BackgroundPolicy.test.ts @@ -14,7 +14,7 @@ import * as PubSub from "effect/PubSub"; import * as Ref from "effect/Ref"; import * as Stream from "effect/Stream"; -import { ServerSettingsService } from "../serverSettings.ts"; +import * as ServerSettings from "../serverSettings.ts"; import * as BackgroundPolicy from "./BackgroundPolicy.ts"; import * as HostPowerMonitor from "./HostPowerMonitor.ts"; @@ -55,7 +55,7 @@ function makeReport(overrides: Partial = {}): ClientA function makeLayer( hostPower: HostPowerSnapshot, - settingsOverrides: Parameters[0] = {}, + settingsOverrides: Parameters[0] = {}, ) { const hostLayer = Layer.effect( HostPowerMonitor.HostPowerMonitor, @@ -73,7 +73,7 @@ function makeLayer( }), ); return BackgroundPolicy.layer.pipe( - Layer.provide(Layer.merge(hostLayer, ServerSettingsService.layerTest(settingsOverrides))), + Layer.provide(Layer.merge(hostLayer, ServerSettings.layerTest(settingsOverrides))), ); } @@ -162,7 +162,7 @@ describe("BackgroundPolicy", () => { }), ); const layer = BackgroundPolicy.layer.pipe( - Layer.provide(Layer.merge(hostLayer, ServerSettingsService.layerTest())), + Layer.provide(Layer.merge(hostLayer, ServerSettings.layerTest())), ); yield* Effect.gen(function* () { diff --git a/apps/server/src/background/BackgroundPolicy.ts b/apps/server/src/background/BackgroundPolicy.ts index ad74bf134a89..49e1c7c20612 100644 --- a/apps/server/src/background/BackgroundPolicy.ts +++ b/apps/server/src/background/BackgroundPolicy.ts @@ -22,7 +22,7 @@ import * as Scope from "effect/Scope"; import * as Semaphore from "effect/Semaphore"; import * as Stream from "effect/Stream"; -import { ServerSettingsService } from "../serverSettings.ts"; +import * as ServerSettings from "../serverSettings.ts"; import { subscribeBeforeSnapshot } from "../utils/subscribeBeforeSnapshot.ts"; import * as HostPowerMonitor from "./HostPowerMonitor.ts"; @@ -211,7 +211,7 @@ function computeSnapshot(input: { /** @public Service construction is part of the canonical Effect module API. */ export const make = Effect.fn("background.policy.make")(function* () { const hostPowerMonitor = yield* HostPowerMonitor.HostPowerMonitor; - const serverSettings = yield* ServerSettingsService; + const serverSettings = yield* ServerSettings.ServerSettingsService; const leasesRef = yield* Ref.make(new Map()); const changes = yield* PubSub.sliding(1); const publishMutex = yield* Semaphore.make(1); diff --git a/apps/server/src/cli/project.test.ts b/apps/server/src/cli/project.test.ts index 200d4a546008..1a489bdcb64a 100644 --- a/apps/server/src/cli/project.test.ts +++ b/apps/server/src/cli/project.test.ts @@ -27,14 +27,14 @@ import { Command } from "effect/unstable/cli"; import { cli } from "../binCli.ts"; import * as ServerConfig from "../config.ts"; -import { EventSinkV2 } from "../orchestration-v2/EventSink.ts"; +import * as EventSink from "../orchestration-v2/EventSink.ts"; import * as EventStore from "../orchestration-v2/EventStore.ts"; import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; import { OrchestrationV2EventSinkLayerLive, ProjectServiceLayerLive, } from "../orchestration-v2/runtimeLayer.ts"; -import { layerConfig as SqlitePersistenceLayerLive } from "../persistence/Layers/Sqlite.ts"; +import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; import * as ProjectFaviconResolver from "../project/ProjectFaviconResolver.ts"; import * as ProjectService from "../project/ProjectService.ts"; @@ -96,7 +96,7 @@ const readProjects = (baseDir: string) => Layer.provideMerge(ProjectFaviconResolver.layer), Layer.provideMerge(T3ProjectFileLoader.layer), Layer.provideMerge(WorkspacePaths.layer), - Layer.provideMerge(SqlitePersistenceLayerLive), + Layer.provideMerge(SqlitePersistence.layerConfig), Layer.provideMerge(NodeServices.layer), Layer.provide(ServerConfig.layer(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, config.logLevel)), @@ -175,7 +175,7 @@ const makeThreadPersistenceLayer = Effect.fn("ProjectCliTest.makeThreadPersisten ProjectionStore.layer, EventStore.layer, ).pipe( - Layer.provideMerge(SqlitePersistenceLayerLive), + Layer.provideMerge(SqlitePersistence.layerConfig), Layer.provideMerge(NodeServices.layer), Layer.provide(ServerConfig.layer(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, config.logLevel)), @@ -195,7 +195,7 @@ const seedNativeThreads = Effect.fn("ProjectCliTest.seedNativeThreads")(function const createdAt = DateTime.makeUnsafe("2026-09-04T12:00:00.000Z"); const providerInstanceId = ProviderInstanceId.make("codex"); yield* Effect.gen(function* () { - const eventSink = yield* EventSinkV2; + const eventSink = yield* EventSink.EventSinkV2; yield* eventSink.write({ commandId: CommandId.make("project-cli-seed-threads"), events: threads.map(({ id, projectId, archived }) => { diff --git a/apps/server/src/cli/project.ts b/apps/server/src/cli/project.ts index 2ed34e80a313..c0d3c26b4461 100644 --- a/apps/server/src/cli/project.ts +++ b/apps/server/src/cli/project.ts @@ -24,7 +24,7 @@ import * as HttpApiClient from "effect/unstable/httpapi/HttpApiClient"; import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts"; import * as ServerConfig from "../config.ts"; -import { layerConfig as SqlitePersistenceLayerLive } from "../persistence/Layers/Sqlite.ts"; +import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; import { ProjectServiceLayerLive } from "../orchestration-v2/runtimeLayer.ts"; import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; import * as ProjectFaviconResolver from "../project/ProjectFaviconResolver.ts"; @@ -206,7 +206,7 @@ const ProjectCliRuntimeLive = ProjectServiceLayerLive.pipe( ), ), Layer.provideMerge(WorkspacePaths.layer), - Layer.provideMerge(SqlitePersistenceLayerLive), + Layer.provideMerge(SqlitePersistence.layerConfig), ); const PROJECT_CLI_LIVE_SERVER_TIMEOUT = Duration.seconds(1); diff --git a/apps/server/src/cli/server.ts b/apps/server/src/cli/server.ts index bc4b3f45706c..298109abdb5f 100644 --- a/apps/server/src/cli/server.ts +++ b/apps/server/src/cli/server.ts @@ -1,21 +1,21 @@ import * as Effect from "effect/Effect"; import { Command, GlobalFlag } from "effect/unstable/cli"; -import { ServerConfig, type StartupPresentation } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import { runServer } from "../server.ts"; import { type CliServerFlags, resolveServerConfig, sharedServerCommandFlags } from "./config.ts"; export const runServerCommand = ( flags: CliServerFlags, options?: { - readonly startupPresentation?: StartupPresentation; + readonly startupPresentation?: ServerConfig.StartupPresentation; readonly forceAutoBootstrapProjectFromCwd?: boolean; }, ) => Effect.gen(function* () { const logLevel = yield* GlobalFlag.LogLevel; const config = yield* resolveServerConfig(flags, logLevel, options); - return yield* runServer.pipe(Effect.provideService(ServerConfig, config)); + return yield* runServer.pipe(Effect.provideService(ServerConfig.ServerConfig, config)); }); export const startCommand = Command.make("start", { ...sharedServerCommandFlags }).pipe( diff --git a/apps/server/src/cloud/CliState.test.ts b/apps/server/src/cloud/CliState.test.ts index d59b89875e79..fe756b416f06 100644 --- a/apps/server/src/cloud/CliState.test.ts +++ b/apps/server/src/cloud/CliState.test.ts @@ -5,7 +5,7 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import * as CliState from "./CliState.ts"; import { CLOUD_ENDPOINT_CONFIRMED_ORIGIN, diff --git a/apps/server/src/desktopUpdate/DesktopAppUpdate.ts b/apps/server/src/desktopUpdate/DesktopAppUpdate.ts index dc29375f0a47..d3c82fd18315 100644 --- a/apps/server/src/desktopUpdate/DesktopAppUpdate.ts +++ b/apps/server/src/desktopUpdate/DesktopAppUpdate.ts @@ -15,7 +15,7 @@ import * as Ref from "effect/Ref"; import * as Result from "effect/Result"; import * as Stream from "effect/Stream"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import * as DesktopTelemetryReceiver from "../resourceTelemetry/DesktopTelemetryReceiver.ts"; /** Backstop for a desktop updater that hangs without ever reporting a @@ -63,7 +63,7 @@ export class DesktopAppUpdate extends Context.Service< >()("t3/desktopUpdate/DesktopAppUpdate") {} export const make = Effect.fn("desktopUpdate.desktopAppUpdate.make")(function* () { - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; const crypto = yield* Crypto.Crypto; const receiver = yield* DesktopTelemetryReceiver.DesktopTelemetryReceiver; const inFlight = yield* Ref.make(false); diff --git a/apps/server/src/device/DeviceHubProxy.test.ts b/apps/server/src/device/DeviceHubProxy.test.ts index d80b0e714645..9fde32d87289 100644 --- a/apps/server/src/device/DeviceHubProxy.test.ts +++ b/apps/server/src/device/DeviceHubProxy.test.ts @@ -9,14 +9,8 @@ import { import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import { HttpClient, HttpClientResponse, HttpRouter } from "effect/unstable/http"; -import { - EnvironmentAuth, - ServerAuthMissingCredentialError, - ServerAuthSessionCredentialValidationError, - type ServerAuthCredentialError, - type ServerAuthInternalError, -} from "../auth/EnvironmentAuth.ts"; -import { DeviceService } from "./DeviceService.ts"; +import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts"; +import * as DeviceService from "./DeviceService.ts"; import { deviceHubProxyRouteLayer } from "./DeviceHubProxy.ts"; const disposers: Array<() => Promise> = []; @@ -27,7 +21,7 @@ afterEach(async () => { const fixture = ( scopes: ReadonlyArray, fail = false, - authError?: ServerAuthCredentialError | ServerAuthInternalError, + authError?: EnvironmentAuth.ServerAuthCredentialError | EnvironmentAuth.ServerAuthInternalError, ) => { let finalized = 0; const requests: string[] = []; @@ -44,7 +38,7 @@ const fixture = ( const { handler, dispose } = HttpRouter.toWebHandler( deviceHubProxyRouteLayer.pipe( Layer.provideMerge( - Layer.succeed(EnvironmentAuth, { + Layer.succeed(EnvironmentAuth.EnvironmentAuth, { authenticateWebSocketUpgrade: () => authError ? Effect.fail(authError) @@ -54,13 +48,13 @@ const fixture = ( method: "bearer-access-token", scopes, }), - } as unknown as EnvironmentAuth["Service"]), + } as unknown as EnvironmentAuth.EnvironmentAuth["Service"]), ), Layer.provideMerge( - Layer.succeed(DeviceService, { + Layer.succeed(DeviceService.DeviceService, { currentReadiness: () => Effect.succeed({ hostId: LOCAL_DEVICE_HOST_ID, hub: { origin: "http://hub.test" } }), - } as DeviceService["Service"]), + } as DeviceService.DeviceService["Service"]), ), Layer.provideMerge(Layer.succeed(HttpClient.HttpClient, client)), ), @@ -151,9 +145,9 @@ describe("device hub proxy", () => { }); it.each([ - [new ServerAuthMissingCredentialError({}), 401], + [new EnvironmentAuth.ServerAuthMissingCredentialError({}), 401], [ - new ServerAuthSessionCredentialValidationError({ + new EnvironmentAuth.ServerAuthSessionCredentialValidationError({ cause: new Error("private credential diagnostic"), }), 500, diff --git a/apps/server/src/device/DeviceMultiHost.test.ts b/apps/server/src/device/DeviceMultiHost.test.ts index 7a40e1e53c72..33b63c96b7b5 100644 --- a/apps/server/src/device/DeviceMultiHost.test.ts +++ b/apps/server/src/device/DeviceMultiHost.test.ts @@ -4,13 +4,13 @@ import * as Deferred from "effect/Deferred"; import * as Fiber from "effect/Fiber"; import * as Effect from "effect/Effect"; import { HttpClient, HttpClientResponse } from "effect/unstable/http"; -import { ServerSettingsService } from "../serverSettings.ts"; -import { DeviceHostError, DeviceHost } from "./DeviceHost.ts"; -import { makeWithHosts } from "./DeviceService.ts"; +import * as ServerSettings from "../serverSettings.ts"; +import * as DeviceHost from "./DeviceHost.ts"; +import * as DeviceService from "./DeviceService.ts"; it.effect("keeps hosts independent when serials collide and another host fails", () => Effect.gen(function* () { - const host = (id: string, failed = false): DeviceHost["Service"] => { + const host = (id: string, failed = false): DeviceHost.DeviceHost["Service"] => { const ready = { nodePath: process.execPath, hub: { origin: `http://${id}` }, @@ -32,7 +32,11 @@ it.effect("keeps hosts independent when serials collide and another host fails", ensureReady: () => failed ? Effect.fail( - new DeviceHostError({ hostId: id, step: "connect", cause: new Error("offline") }), + new DeviceHost.DeviceHostError({ + hostId: id, + step: "connect", + cause: new Error("offline"), + }), ) : Effect.succeed(ready), ensureAgentReady: () => Effect.succeed(ready), @@ -65,7 +69,7 @@ it.effect("keeps hosts independent when serials collide and another host fails", const writeStarted = yield* Deferred.make(); const finishWrite = yield* Deferred.make(); const order: string[] = []; - const service = yield* makeWithHosts(hosts, undefined, () => + const service = yield* DeviceService.makeWithHosts(hosts, undefined, () => Effect.gen(function* () { order.push("write started"); yield* Deferred.succeed(writeStarted, undefined); @@ -120,7 +124,10 @@ it.effect("keeps hosts independent when serials collide and another host fails", expect((yield* service.state).hostStatuses).toEqual({}); }).pipe( Effect.provide( - ServerSettingsService.layerTest({ enableDeviceSupport: true, enableAgentDeviceAccess: true }), + ServerSettings.layerTest({ + enableDeviceSupport: true, + enableAgentDeviceAccess: true, + }), ), ), ); diff --git a/apps/server/src/device/DeviceService.test.ts b/apps/server/src/device/DeviceService.test.ts index 3454797453d8..83a9d2f7e15b 100644 --- a/apps/server/src/device/DeviceService.test.ts +++ b/apps/server/src/device/DeviceService.test.ts @@ -16,11 +16,11 @@ import * as Ref from "effect/Ref"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; import { HttpClient, HttpClientResponse } from "effect/unstable/http"; -import { ServerSettingsService } from "../serverSettings.ts"; +import * as ServerSettings from "../serverSettings.ts"; import * as DeviceHost from "./DeviceHost.ts"; import { NodeRuntimeUnavailableError } from "@t3tools/shared/nodeRuntime"; -import { type DeviceService, makeWithHosts, stateStream } from "./DeviceService.ts"; +import * as DeviceService from "./DeviceService.ts"; const decodeJson = Schema.decodeUnknownSync(Schema.fromJsonString(Schema.Unknown)); @@ -41,16 +41,14 @@ describe("DeviceService.stateStream", () => { Effect.gen(function* () { const pubsub = yield* PubSub.unbounded(); const current = yield* Ref.make(baseState); - const service: Pick = { + const service: Pick = { state: Ref.get(current), subscribe: PubSub.subscribe(pubsub), }; - const collected = yield* stateStream(service as DeviceService["Service"]).pipe( - Stream.take(3), - Stream.runCollect, - Effect.forkChild, - ); + const collected = yield* DeviceService.stateStream( + service as DeviceService.DeviceService["Service"], + ).pipe(Stream.take(3), Stream.runCollect, Effect.forkChild); yield* Effect.yieldNow; for (const revision of [1, 2]) { const next = { ...baseState, revision, hostStatus: "ready" as const }; @@ -69,7 +67,7 @@ const fixture = Effect.fn("fixture")(function* ( failListAfterShutdown = false, runtimeFailure?: NodeRuntimeUnavailableError | DeviceHost.DeviceHostError, inspectError = false, - installTool?: Parameters[3], + installTool?: Parameters[3], ) { const settings = yield* Ref.make(DEFAULT_SERVER_SETTINGS); const starts: string[] = []; @@ -131,7 +129,7 @@ const fixture = Effect.fn("fixture")(function* ( starts.push("stop"); }), }; - const service = yield* makeWithHosts( + const service = yield* DeviceService.makeWithHosts( new Map([[host.id, host]]), undefined, undefined, @@ -139,8 +137,8 @@ const fixture = Effect.fn("fixture")(function* ( ).pipe( Effect.provideService(DeviceHost.DeviceHost, host), Effect.provideService( - ServerSettingsService, - ServerSettingsService.of({ + ServerSettings.ServerSettingsService, + ServerSettings.ServerSettingsService.of({ updateProviderInstance: () => Effect.die("Unexpected provider mutation"), withSettingsSnapshot: (use) => Effect.flatMap(Ref.get(settings), use), start: Effect.void, @@ -473,7 +471,7 @@ it.effect.each(["shutdown", "close"] as const)( return HttpClientResponse.fromWeb(request, Response.json({ ok: true, id: deviceId })); }), ); - const service = yield* makeWithHosts(new Map([[host.id, host]])).pipe( + const service = yield* DeviceService.makeWithHosts(new Map([[host.id, host]])).pipe( Effect.provideService(HttpClient.HttpClient, http), ); const input = { threadId, deviceId, platform: "ios" as const }; @@ -486,10 +484,7 @@ it.effect.each(["shutdown", "close"] as const)( yield* service.open(input); expect(capture).toBe(2); expect((yield* service.state).sessions).toHaveLength(1); - }).pipe( - Effect.provide(ServerSettingsService.layerTest({ enableDeviceSupport: true })), - Effect.scoped, - ), + }).pipe(Effect.provide(ServerSettings.layerTest({ enableDeviceSupport: true })), Effect.scoped), ); it.effect.each([ @@ -568,7 +563,7 @@ it.effect.each([ throw new Error(`Unexpected hub path: ${path}`); }), ); - const service = yield* makeWithHosts(new Map([[host.id, host]])).pipe( + const service = yield* DeviceService.makeWithHosts(new Map([[host.id, host]])).pipe( Effect.provideService(HttpClient.HttpClient, http), ); yield* service.list; @@ -587,10 +582,7 @@ it.effect.each([ (yield* service.state).devices.find((device) => device.id === deviceId)?.booted, ).toBe(true); } - }).pipe( - Effect.provide(ServerSettingsService.layerTest({ enableDeviceSupport: true })), - Effect.scoped, - ), + }).pipe(Effect.provide(ServerSettings.layerTest({ enableDeviceSupport: true })), Effect.scoped), ); it.effect("retry keeps device and agent consent unchanged", () => diff --git a/apps/server/src/git/linkCreatedPullRequest.test.ts b/apps/server/src/git/linkCreatedPullRequest.test.ts index 826c786a2c14..779fb002db9c 100644 --- a/apps/server/src/git/linkCreatedPullRequest.test.ts +++ b/apps/server/src/git/linkCreatedPullRequest.test.ts @@ -12,19 +12,15 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Ref from "effect/Ref"; -import { - OrchestratorV2, - type OrchestratorV2Shape, - OrchestratorDispatchError, -} from "../orchestration-v2/Orchestrator.ts"; +import * as Orchestrator from "../orchestration-v2/Orchestrator.ts"; import { type PullRequestTestThread, v2PullRequestThread, } from "../orchestration-v2/testkit/pullRequestFixtures.ts"; import * as ProjectStore from "../orchestration-v2/ProjectStore.ts"; -import { ProjectService } from "../project/ProjectService.ts"; +import * as ProjectService from "../project/ProjectService.ts"; import { refreshPushedPullRequests } from "./refreshPushedPullRequests.ts"; -import { PullRequestService } from "../pullRequest/PullRequestService.ts"; +import * as PullRequestService from "../pullRequest/PullRequestService.ts"; import { createdPullRequestKey, linkCreatedPullRequest } from "./linkCreatedPullRequest.ts"; const PROJECT_ID = ProjectId.make("project-1"); @@ -76,14 +72,14 @@ function prResult(pr: GitRunStackedActionResult["pr"]): Pick Layer.mergeAll( - Layer.mock(ProjectService)({ + Layer.mock(ProjectService.ProjectService)({ getShell: () => Effect.succeedSome(project), }), - Layer.mock(OrchestratorV2)({ + Layer.mock(Orchestrator.OrchestratorV2)({ getThreadShell: () => Effect.succeed(threadShell ? v2PullRequestThread(threadShell) : null), dispatch, }), @@ -91,7 +87,7 @@ const makeDependencies = ( const recordingDispatch = Effect.fn("recordingDispatch")(function* () { const commands = yield* Ref.make>([]); - const dispatch: OrchestratorV2Shape["dispatch"] = (command) => + const dispatch: Orchestrator.OrchestratorV2Shape["dispatch"] = (command) => Ref.update(commands, (recorded) => [...recorded, command]).pipe( Effect.as({ sequence: 1, storedEvents: [] }), ); @@ -201,9 +197,9 @@ describe("linkCreatedPullRequest", () => { it.effect("swallows an already-linked rejection and other dispatch failures", () => Effect.gen(function* () { - const rejecting: OrchestratorV2Shape["dispatch"] = (command) => + const rejecting: Orchestrator.OrchestratorV2Shape["dispatch"] = (command) => Effect.fail( - new OrchestratorDispatchError({ + new Orchestrator.OrchestratorDispatchError({ commandId: command.commandId, commandType: command.type, cause: "already linked", @@ -234,13 +230,13 @@ it.effect( Effect.gen(function* () { const refreshed: string[] = []; const dependencies = Layer.mergeAll( - Layer.mock(OrchestratorV2)({ + Layer.mock(Orchestrator.OrchestratorV2)({ getThreadShell: () => Effect.succeed(v2PullRequestThread(thread)), }), Layer.mock(ProjectStore.ProjectStoreV2)({ listShells: () => Effect.succeed([project]), }), - Layer.mock(PullRequestService)({ + Layer.mock(PullRequestService.PullRequestService)({ refreshAfterTurn: (id) => Effect.sync(() => { refreshed.push(id); diff --git a/apps/server/src/git/linkCreatedPullRequest.ts b/apps/server/src/git/linkCreatedPullRequest.ts index beb01492b6ba..f95ded1e8864 100644 --- a/apps/server/src/git/linkCreatedPullRequest.ts +++ b/apps/server/src/git/linkCreatedPullRequest.ts @@ -12,7 +12,7 @@ import * as Cause from "effect/Cause"; import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; -import { OrchestratorV2 } from "../orchestration-v2/Orchestrator.ts"; +import * as Orchestrator from "../orchestration-v2/Orchestrator.ts"; import * as ProjectService from "../project/ProjectService.ts"; export interface CreatedPullRequestKey { @@ -61,9 +61,9 @@ export const linkCreatedPullRequest = (input: { readonly threadId: ThreadId; readonly result: Pick; readonly commandId: Effect.Effect; -}): Effect.Effect => +}): Effect.Effect => Effect.gen(function* () { - const engine = yield* OrchestratorV2; + const engine = yield* Orchestrator.OrchestratorV2; const projects = yield* ProjectService.ProjectService; const thread = yield* engine .getThreadShell(input.threadId) diff --git a/apps/server/src/mcp/McpHttpServer.test.ts b/apps/server/src/mcp/McpHttpServer.test.ts index f9b900e0e5eb..69801ee06100 100644 --- a/apps/server/src/mcp/McpHttpServer.test.ts +++ b/apps/server/src/mcp/McpHttpServer.test.ts @@ -1,5 +1,5 @@ -import { OrchestratorV2 } from "../orchestration-v2/Orchestrator.ts"; -import { ProjectionStoreV2 } from "../orchestration-v2/ProjectionStore.ts"; +import * as Orchestrator from "../orchestration-v2/Orchestrator.ts"; +import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; import { expect, it } from "@effect/vitest"; import { NodeHttpServer } from "@effect/platform-node"; import * as NodeServices from "@effect/platform-node/NodeServices"; @@ -14,7 +14,7 @@ import * as Stream from "effect/Stream"; import { McpProtocol, McpSchema, McpServer } from "effect/unstable/ai"; import { HttpBody, HttpClient, HttpRouter, HttpServerResponse } from "effect/unstable/http"; -import { ProjectService } from "../project/ProjectService.ts"; +import * as ProjectService from "../project/ProjectService.ts"; import * as ServerConfig from "../config.ts"; import * as McpHttpServer from "./McpHttpServer.ts"; import * as McpInvocationContext from "./McpInvocationContext.ts"; @@ -55,9 +55,9 @@ const PullRequestsTestLayer = McpHttpServer.PullRequestsToolkitRegistrationLive. Layer.provideMerge(McpServer.McpServer.layer), Layer.provide( Layer.mergeAll( - Layer.mock(ProjectService)({}), - Layer.mock(OrchestratorV2)({}), - Layer.mock(ProjectionStoreV2)({}), + Layer.mock(ProjectService.ProjectService)({}), + Layer.mock(Orchestrator.OrchestratorV2)({}), + Layer.mock(ProjectionStore.ProjectionStoreV2)({}), NodeServices.layer, ), ), diff --git a/apps/server/src/mcp/McpSessionRegistry.testkit.ts b/apps/server/src/mcp/McpSessionRegistry.testkit.ts index 623e8208a7a5..1ac02357a878 100644 --- a/apps/server/src/mcp/McpSessionRegistry.testkit.ts +++ b/apps/server/src/mcp/McpSessionRegistry.testkit.ts @@ -2,11 +2,11 @@ import { EnvironmentId } from "@t3tools/contracts"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; -import { McpSessionRegistry } from "./McpSessionRegistry.ts"; +import * as McpSessionRegistry from "./McpSessionRegistry.ts"; export const layer = Layer.succeed( - McpSessionRegistry, - McpSessionRegistry.of({ + McpSessionRegistry.McpSessionRegistry, + McpSessionRegistry.McpSessionRegistry.of({ issue: ({ threadId, providerInstanceId }) => Effect.succeed({ config: { diff --git a/apps/server/src/mcp/OrchestratorMcpService.activity.test.ts b/apps/server/src/mcp/OrchestratorMcpService.activity.test.ts index 11c73ace0b6d..06eff1e6f8b1 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.activity.test.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.activity.test.ts @@ -14,18 +14,12 @@ import * as Layer from "effect/Layer"; import * as NodeCrypto from "@effect/platform-node/NodeCrypto"; import { expect, it } from "vite-plus/test"; -import { ProviderAdapterRegistryV2 } from "../orchestration-v2/ProviderAdapterRegistry.ts"; -import { ProviderRegistry } from "../provider/Services/ProviderRegistry.ts"; -import { ScheduledTaskService } from "../scheduledTasks/ScheduledTaskService.ts"; -import { - ThreadManagementService, - ThreadManagementThreadNotFoundError, -} from "../orchestration-v2/ThreadManagementService.ts"; +import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts"; +import * as ProviderRegistry from "../provider/Services/ProviderRegistry.ts"; +import * as ScheduledTaskService from "../scheduledTasks/ScheduledTaskService.ts"; +import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; import type * as McpInvocationContext from "./McpInvocationContext.ts"; -import { - layer as orchestratorMcpServiceLayer, - OrchestratorMcpService, -} from "./OrchestratorMcpService.ts"; +import * as OrchestratorMcpService from "./OrchestratorMcpService.ts"; const environmentId = EnvironmentId.make("environment-mcp-orchestrator-detail"); const projectId = ProjectId.make("project-mcp-orchestrator-detail"); @@ -124,32 +118,32 @@ it("readThread prefers activity-run status over a newer cancelled queued run", a updatedAt: now, } as unknown as OrchestrationV2ThreadProjection; - const layer = orchestratorMcpServiceLayer.pipe( + const layer = OrchestratorMcpService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getTimelinePage: () => Effect.succeed({ items: [], totalItems: 0, hasMore: false }), getThreadRecords: (threadId) => threadId === parentThreadId ? Effect.succeed(projection) : Effect.die(`unexpected thread ${threadId}`), - } satisfies Partial), - Layer.mock(ProviderRegistry)({ + } satisfies Partial), + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([]), - } satisfies Partial), - Layer.mock(ScheduledTaskService)({ + } satisfies Partial), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({ list: () => Effect.succeed({ tasks: [] }), - } satisfies Partial), - Layer.mock(ProviderAdapterRegistryV2)({ + } satisfies Partial), + Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({ list: () => Effect.succeed([]), - } satisfies Partial), + } satisfies Partial), NodeCrypto.layer, ), ), ); await Effect.gen(function* () { - const service = yield* OrchestratorMcpService; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; const result = yield* service.readThread(makeScope(), { threadId: parentThreadId }); expect(result.thread.status).toBe("running"); expect(result.thread.latestRunId).toBe(cancelledRunId); @@ -177,32 +171,32 @@ it("readThread prefers waiting activity status over a newer cancelled queued run updatedAt: now, } as unknown as OrchestrationV2ThreadProjection; - const layer = orchestratorMcpServiceLayer.pipe( + const layer = OrchestratorMcpService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getTimelinePage: () => Effect.succeed({ items: [], totalItems: 0, hasMore: false }), getThreadRecords: (threadId) => threadId === parentThreadId ? Effect.succeed(projection) : Effect.die(`unexpected thread ${threadId}`), - } satisfies Partial), - Layer.mock(ProviderRegistry)({ + } satisfies Partial), + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([]), - } satisfies Partial), - Layer.mock(ScheduledTaskService)({ + } satisfies Partial), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({ list: () => Effect.succeed({ tasks: [] }), - } satisfies Partial), - Layer.mock(ProviderAdapterRegistryV2)({ + } satisfies Partial), + Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({ list: () => Effect.succeed([]), - } satisfies Partial), + } satisfies Partial), NodeCrypto.layer, ), ), ); await Effect.gen(function* () { - const service = yield* OrchestratorMcpService; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; const result = yield* service.readThread(makeScope(), { threadId: parentThreadId }); expect(result.thread.status).toBe("waiting"); expect(result.thread.activeRunId).toBe(activeRunId); @@ -287,33 +281,33 @@ it("taskStatus returns task.providerInstanceId rather than the driver kind", asy updatedAt: now, } as unknown as OrchestrationV2ThreadProjection; - const layer = orchestratorMcpServiceLayer.pipe( + const layer = OrchestratorMcpService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getTimelinePage: () => Effect.succeed({ items: [], totalItems: 0, hasMore: false }), getThreadRecords: (threadId) => { if (threadId === parentThreadId) return Effect.succeed(parentProjection); if (threadId === childThreadId) return Effect.succeed(childProjection); return Effect.die(`unexpected thread ${threadId}`); }, - } satisfies Partial), - Layer.mock(ProviderRegistry)({ + } satisfies Partial), + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([]), - } satisfies Partial), - Layer.mock(ScheduledTaskService)({ + } satisfies Partial), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({ list: () => Effect.succeed({ tasks: [] }), - } satisfies Partial), - Layer.mock(ProviderAdapterRegistryV2)({ + } satisfies Partial), + Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({ list: () => Effect.succeed([]), - } satisfies Partial), + } satisfies Partial), NodeCrypto.layer, ), ), ); await Effect.gen(function* () { - const service = yield* OrchestratorMcpService; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; const result = yield* service.taskStatus(makeScope(), taskId); expect(result.providerInstanceId).toBe(customCodexInstanceId); expect(result.providerInstanceId).not.toBe(ProviderInstanceId.make(String(codexDriver))); @@ -406,10 +400,10 @@ it("readThread reaches a thread the user attached as context, but not one an age updatedAt: now, }) as unknown as OrchestrationV2ThreadProjection; - const layer = orchestratorMcpServiceLayer.pipe( + const layer = OrchestratorMcpService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: (threadId) => { if (threadId === parentThreadId) return Effect.succeed(parentProjection); if (threadId === foreignThreadId || threadId === agentOnlyThreadId) { @@ -425,28 +419,28 @@ it("readThread reaches a thread the user attached as context, but not one an age }), getProjectThreadRecords: (input) => Effect.fail( - new ThreadManagementThreadNotFoundError({ + new ThreadManagementService.ThreadManagementThreadNotFoundError({ projectId: input.projectId, threadId: input.threadId, }), ), - } satisfies Partial), - Layer.mock(ProviderRegistry)({ + } satisfies Partial), + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([]), - } satisfies Partial), - Layer.mock(ScheduledTaskService)({ + } satisfies Partial), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({ list: () => Effect.succeed({ tasks: [] }), - } satisfies Partial), - Layer.mock(ProviderAdapterRegistryV2)({ + } satisfies Partial), + Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({ list: () => Effect.succeed([]), - } satisfies Partial), + } satisfies Partial), NodeCrypto.layer, ), ), ); await Effect.gen(function* () { - const service = yield* OrchestratorMcpService; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; const attached = yield* service.readThread(makeScope(), { threadId: foreignThreadId }); expect(attached.thread.threadId).toBe(foreignThreadId); expect(attached.items.map((item) => item.text)).toEqual(["Foreign thread said hello"]); diff --git a/apps/server/src/mcp/OrchestratorMcpService.test.ts b/apps/server/src/mcp/OrchestratorMcpService.test.ts index 27faac5c770d..3f927c283ef8 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.test.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.test.ts @@ -16,14 +16,11 @@ import * as Layer from "effect/Layer"; import * as Ref from "effect/Ref"; import type { ProviderAdapterV2Shape } from "../orchestration-v2/ProviderAdapter.ts"; -import { - ProviderAdapterRegistryLookupError, - ProviderAdapterRegistryV2, -} from "../orchestration-v2/ProviderAdapterRegistry.ts"; -import { ThreadManagementService } from "../orchestration-v2/ThreadManagementService.ts"; -import { ProviderRegistry } from "../provider/Services/ProviderRegistry.ts"; +import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts"; +import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; +import * as ProviderRegistry from "../provider/Services/ProviderRegistry.ts"; import { buildUnavailableProviderSnapshot } from "../provider/unavailableProviderSnapshot.ts"; -import { ScheduledTaskService } from "../scheduledTasks/ScheduledTaskService.ts"; +import * as ScheduledTaskService from "../scheduledTasks/ScheduledTaskService.ts"; import type { McpInvocationScope } from "./McpInvocationContext.ts"; import * as OrchestratorMcpService from "./OrchestratorMcpService.ts"; @@ -64,7 +61,7 @@ describe("OrchestratorMcpService", () => { let hasNestedWork = true; const dependencies = Layer.mergeAll( NodeServices.layer, - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: (threadId) => Effect.succeed( threadId === parentThreadId @@ -100,9 +97,11 @@ describe("OrchestratorMcpService", () => { ), ), }), - Layer.mock(ProviderRegistry)({ getProviders: Effect.succeed([]) }), - Layer.mock(ProviderAdapterRegistryV2)({ list: () => Effect.succeed([]) }), - Layer.mock(ScheduledTaskService)({}), + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([]) }), + Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({ + list: () => Effect.succeed([]), + }), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({}), ); const scope: McpInvocationScope = { environmentId: EnvironmentId.make("environment:mcp-ack"), @@ -167,7 +166,7 @@ describe("OrchestratorMcpService", () => { } as unknown as OrchestrationV2ThreadProjection; const dependencies = Layer.mergeAll( NodeServices.layer, - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: (threadId) => Effect.succeed(threadId === parentThreadId ? parentProjection : childProjection), dispatch: (command) => @@ -175,9 +174,11 @@ describe("OrchestratorMcpService", () => { Effect.as({} as never), ), }), - Layer.mock(ProviderRegistry)({ getProviders: Effect.succeed([]) }), - Layer.mock(ProviderAdapterRegistryV2)({ list: () => Effect.succeed([]) }), - Layer.mock(ScheduledTaskService)({}), + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([]) }), + Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({ + list: () => Effect.succeed([]), + }), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({}), ); const scope: McpInvocationScope = { environmentId: EnvironmentId.make("environment:mcp-cancel"), @@ -233,7 +234,7 @@ describe("OrchestratorMcpService", () => { } as unknown as OrchestrationV2ThreadProjection; const dependencies = Layer.mergeAll( NodeServices.layer, - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: (threadId) => Effect.succeed(threadId === parentThreadId ? parentProjection : childProjection), dispatch: (command) => @@ -241,9 +242,11 @@ describe("OrchestratorMcpService", () => { Effect.andThen(Effect.fail(new Error("simulated interrupt failure") as never)), ), }), - Layer.mock(ProviderRegistry)({ getProviders: Effect.succeed([]) }), - Layer.mock(ProviderAdapterRegistryV2)({ list: () => Effect.succeed([]) }), - Layer.mock(ScheduledTaskService)({}), + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([]) }), + Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({ + list: () => Effect.succeed([]), + }), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({}), ); const scope: McpInvocationScope = { environmentId: EnvironmentId.make("environment:mcp-cancel-failed"), @@ -302,7 +305,7 @@ describe("OrchestratorMcpService", () => { } as unknown as OrchestrationV2ThreadProjection; const dependencies = Layer.mergeAll( NodeServices.layer, - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: (threadId) => Effect.succeed(threadId === parentThreadId ? parentProjection : childProjection), dispatch: (command) => @@ -314,9 +317,11 @@ describe("OrchestratorMcpService", () => { ), ), }), - Layer.mock(ProviderRegistry)({ getProviders: Effect.succeed([]) }), - Layer.mock(ProviderAdapterRegistryV2)({ list: () => Effect.succeed([]) }), - Layer.mock(ScheduledTaskService)({}), + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([]) }), + Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({ + list: () => Effect.succeed([]), + }), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({}), ); const scope: McpInvocationScope = { environmentId: EnvironmentId.make("environment:mcp-cancel-dispose-failed"), @@ -386,13 +391,15 @@ describe("OrchestratorMcpService provider resolution", () => { const adapterRegistryLayer = (instanceIds: ReadonlyArray) => Layer.succeed( - ProviderAdapterRegistryV2, - ProviderAdapterRegistryV2.of({ + ProviderAdapterRegistry.ProviderAdapterRegistryV2, + ProviderAdapterRegistry.ProviderAdapterRegistryV2.of({ list: () => Effect.succeed(instanceIds), get: (instanceId) => instanceIds.includes(instanceId) ? Effect.succeed({ instanceId } as unknown as ProviderAdapterV2Shape) - : Effect.fail(new ProviderAdapterRegistryLookupError({ instanceId })), + : Effect.fail( + new ProviderAdapterRegistry.ProviderAdapterRegistryLookupError({ instanceId }), + ), }), ); @@ -492,10 +499,12 @@ describe("OrchestratorMcpService provider resolution", () => { ]; const dependencies = Layer.mergeAll( NodeServices.layer, - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: () => Effect.succeed(parentProjection([])), }), - Layer.mock(ProviderRegistry)({ getProviders: Effect.sync(() => providers) }), + Layer.mock(ProviderRegistry.ProviderRegistry)({ + getProviders: Effect.sync(() => providers), + }), adapterRegistryLayer([ codexInstanceId, ProviderInstanceId.make("claudeAgent"), @@ -504,7 +513,7 @@ describe("OrchestratorMcpService provider resolution", () => { antigravityInstanceId, disabledAntigravityInstanceId, ]), - Layer.mock(ScheduledTaskService)({}), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({}), ); yield* Effect.gen(function* () { @@ -606,7 +615,7 @@ describe("OrchestratorMcpService provider resolution", () => { const dispatched = yield* Ref.make>([]); const dependencies = Layer.mergeAll( NodeServices.layer, - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: (threadId) => Effect.succeed( threadId === parentThreadId @@ -632,7 +641,7 @@ describe("OrchestratorMcpService provider resolution", () => { } as never), ), }), - Layer.mock(ProviderRegistry)({ + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([ providerSnapshot({ instanceId: codexInstanceId, @@ -647,7 +656,7 @@ describe("OrchestratorMcpService provider resolution", () => { ]), }), adapterRegistryLayer([codexInstanceId, antigravityInstanceId]), - Layer.mock(ScheduledTaskService)({}), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({}), ); yield* Effect.gen(function* () { @@ -699,7 +708,7 @@ describe("OrchestratorMcpService provider resolution", () => { let delegated = false; const dependencies = Layer.mergeAll( NodeServices.layer, - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: (threadId) => Effect.succeed( threadId === parentThreadId @@ -725,7 +734,7 @@ describe("OrchestratorMcpService provider resolution", () => { } as never), ), }), - Layer.mock(ProviderRegistry)({ + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([ providerSnapshot({ instanceId: codexInstanceId, @@ -740,7 +749,7 @@ describe("OrchestratorMcpService provider resolution", () => { ]), }), adapterRegistryLayer([codexInstanceId, antigravityInstanceId]), - Layer.mock(ScheduledTaskService)({}), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({}), ); yield* Effect.gen(function* () { @@ -774,10 +783,10 @@ describe("OrchestratorMcpService provider resolution", () => { }); const dependencies = Layer.mergeAll( NodeServices.layer, - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: () => Effect.succeed(parentProjection([])), }), - Layer.mock(ProviderRegistry)({ + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([ providerSnapshot({ instanceId: codexInstanceId, @@ -788,7 +797,7 @@ describe("OrchestratorMcpService provider resolution", () => { ]), }), adapterRegistryLayer([codexInstanceId]), - Layer.mock(ScheduledTaskService)({}), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({}), ); yield* Effect.gen(function* () { @@ -908,7 +917,7 @@ describe("OrchestratorMcpService provider resolution", () => { let delegated = false; const dependencies = Layer.mergeAll( NodeServices.layer, - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: (threadId) => Effect.succeed( threadId === parentThreadId @@ -934,7 +943,7 @@ describe("OrchestratorMcpService provider resolution", () => { } as never), ), }), - Layer.mock(ProviderRegistry)({ + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([ providerSnapshot({ instanceId: codexInstanceId, @@ -951,7 +960,7 @@ describe("OrchestratorMcpService provider resolution", () => { ]), }), adapterRegistryLayer([codexInstanceId, codexAltInstanceId]), - Layer.mock(ScheduledTaskService)({}), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({}), ); yield* Effect.gen(function* () { diff --git a/apps/server/src/mcp/OrchestratorMcpService.ts b/apps/server/src/mcp/OrchestratorMcpService.ts index d73ae8e8640c..5fdc7eb81c80 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.ts @@ -61,21 +61,14 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; -import { ProviderAdapterRegistryV2 } from "../orchestration-v2/ProviderAdapterRegistry.ts"; +import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts"; import { subagentResultForRun, delegatedTaskProgress, } from "../orchestration-v2/SubagentProjection.ts"; -import { - isActiveRun, - isTerminalRunStatus, - latestActiveRun, - latestRun, - ThreadManagementError, - ThreadManagementService, -} from "../orchestration-v2/ThreadManagementService.ts"; -import { ProviderRegistry } from "../provider/Services/ProviderRegistry.ts"; -import { ScheduledTaskService } from "../scheduledTasks/ScheduledTaskService.ts"; +import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; +import * as ProviderRegistry from "../provider/Services/ProviderRegistry.ts"; +import * as ScheduledTaskService from "../scheduledTasks/ScheduledTaskService.ts"; import type { McpInvocationScope } from "./McpInvocationContext.ts"; const DEFAULT_WAIT_TIMEOUT_MS = 10 * 60 * 1_000; @@ -157,7 +150,7 @@ export class OrchestratorMcpService extends Context.Service< OrchestratorMcpServiceShape >()("t3/mcp/OrchestratorMcpService") {} -const isThreadManagementError = Schema.is(ThreadManagementError); +const isThreadManagementError = Schema.is(ThreadManagementService.ThreadManagementError); function failure(code: OrchestratorMcpFailure["code"], message: string): OrchestratorMcpFailure { return new OrchestratorMcpFailure({ code, message }); @@ -328,7 +321,7 @@ export function hasPendingChildRuns( ): boolean { return childProjection.runs.some( (run) => - !isTerminalRunStatus(run.status) && + !ThreadManagementService.isTerminalRunStatus(run.status) && (delegatedRun === undefined || run.ordinal > delegatedRun.ordinal), ); } @@ -414,7 +407,7 @@ function latestTerminalResultRun( return projection.runs .filter( (run) => - isTerminalRunStatus(run.status) && + ThreadManagementService.isTerminalRunStatus(run.status) && !monitorRunIds.has(run.id) && run.status !== "rolled_back" && (run.id === delegatedRun?.id || run.startedAt !== null), @@ -598,8 +591,8 @@ function threadDetail( projection: Pick, itemCount: number, ): OrchestratorMcpThreadDetail { - const latest = latestRun(projection); - const active = latestActiveRun(projection); + const latest = ThreadManagementService.latestRun(projection); + const active = ThreadManagementService.latestActiveRun(projection); return { threadId: projection.thread.id, projectId: projection.thread.projectId, @@ -759,10 +752,10 @@ function timelineItem(input: { const make = Effect.gen(function* () { const crypto = yield* Crypto.Crypto; - const threadManagement = yield* ThreadManagementService; - const providerRegistry = yield* ProviderRegistry; - const providerAdapters = yield* ProviderAdapterRegistryV2; - const scheduledTasks = yield* ScheduledTaskService; + const threadManagement = yield* ThreadManagementService.ThreadManagementService; + const providerRegistry = yield* ProviderRegistry.ProviderRegistry; + const providerAdapters = yield* ProviderAdapterRegistry.ProviderAdapterRegistryV2; + const scheduledTasks = yield* ScheduledTaskService.ScheduledTaskService; const requireCapability = (scope: McpInvocationScope) => scope.capabilities.has("orchestration") @@ -1129,7 +1122,7 @@ const make = Effect.gen(function* () { task.completionDelivery?.state !== "acknowledged" && task.completionDelivery?.state !== "disposed" ) { - const observingRun = latestActiveRun(parentProjection); + const observingRun = ThreadManagementService.latestActiveRun(parentProjection); const acknowledgementRequestKey = yield* requestKey(undefined); yield* threadManagement .dispatch({ @@ -1365,7 +1358,7 @@ const make = Effect.gen(function* () { yield* requireCapability(scope); const parent = yield* loadProjection(scope.threadId); const parentRun = parent.runs - .filter(isActiveRun) + .filter(ThreadManagementService.isActiveRun) .toSorted((left, right) => right.ordinal - left.ordinal)[0]; if ( parentRun === undefined || @@ -1525,7 +1518,7 @@ const make = Effect.gen(function* () { } satisfies OrchestratorMcpTaskCancelResult; } const child = yield* loadProjection(current.childThreadId); - const activeRun = latestActiveRun(child); + const activeRun = ThreadManagementService.latestActiveRun(child); if (activeRun === undefined) { return yield* failure( "task_not_cancellable", @@ -1569,7 +1562,7 @@ const make = Effect.gen(function* () { Effect.gen(function* () { yield* requireCapability(scope); const parent = yield* loadProjection(scope.threadId); - const parentRun = latestActiveRun(parent); + const parentRun = ThreadManagementService.latestActiveRun(parent); if ( parentRun === undefined || parentRun.rootNodeId === null || @@ -1950,8 +1943,8 @@ export const layer: Layer.Layer< OrchestratorMcpService, never, | Crypto.Crypto - | ThreadManagementService - | ProviderRegistry - | ProviderAdapterRegistryV2 - | ScheduledTaskService + | ThreadManagementService.ThreadManagementService + | ProviderRegistry.ProviderRegistry + | ProviderAdapterRegistry.ProviderAdapterRegistryV2 + | ScheduledTaskService.ScheduledTaskService > = Layer.effect(OrchestratorMcpService, make); diff --git a/apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts b/apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts index 797a0c836210..7d20bf512b10 100644 --- a/apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts +++ b/apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts @@ -50,20 +50,17 @@ import { McpSchema, McpServer } from "effect/unstable/ai"; import { ClaudeProviderCapabilitiesV2 } from "../orchestration-v2/Adapters/ClaudeAdapterV2.ts"; import { CodexProviderCapabilitiesV2 } from "../orchestration-v2/Adapters/CodexAdapterV2.ts"; import { CodexOrchestratorReplayHarness } from "../orchestration-v2/Adapters/CodexAdapterV2.testkit.ts"; -import { EventSinkV2 } from "../orchestration-v2/EventSink.ts"; -import { OrchestratorV2, type OrchestratorV2Shape } from "../orchestration-v2/Orchestrator.ts"; -import { layer as threadManagementServiceLayer } from "../orchestration-v2/ThreadManagementService.ts"; +import * as EventSink from "../orchestration-v2/EventSink.ts"; +import * as Orchestrator from "../orchestration-v2/Orchestrator.ts"; +import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; import { type ProviderAdapterV2Event, ProviderAdapterProtocolError, type ProviderAdapterV2Shape, type ProviderAdapterV2TurnInput, } from "../orchestration-v2/ProviderAdapter.ts"; -import { makeLayer as makeProviderAdapterRegistryLayer } from "../orchestration-v2/ProviderAdapterRegistry.ts"; -import { - type ProviderContinuationRequest, - ProviderContinuationRequests, -} from "../orchestration-v2/ProviderContinuationRequests.ts"; +import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts"; +import * as ProviderContinuationRequests from "../orchestration-v2/ProviderContinuationRequests.ts"; import { checkpointWorkspace } from "../orchestration-v2/testkit/ReplayFixtureWorkspace.ts"; import { makeOrchestratorV2ProviderReplayLayer, @@ -74,7 +71,7 @@ import { materializeReplayTranscriptWorkspace, } from "../orchestration-v2/testkit/ReplayTranscriptNdjson.ts"; import { makeProviderRegistryLayer } from "../provider/testUtils/providerRegistryMock.ts"; -import { ScheduledTaskService } from "../scheduledTasks/ScheduledTaskService.ts"; +import * as ScheduledTaskService from "../scheduledTasks/ScheduledTaskService.ts"; import * as McpHttpServer from "./McpHttpServer.ts"; import * as McpInvocationContext from "./McpInvocationContext.ts"; import { delegatedTaskRun, hasPendingChildRuns } from "./OrchestratorMcpService.ts"; @@ -399,7 +396,7 @@ function makeDeterministicAdapter(input: { } function waitForProjection( - orchestrator: OrchestratorV2Shape, + orchestrator: Orchestrator.OrchestratorV2Shape, threadId: ThreadId, predicate: (projection: OrchestrationV2ThreadProjection) => boolean, ) { @@ -458,8 +455,8 @@ function scheduledTaskFromUpsert(input: ScheduledTaskUpsertInput): ScheduledTask } const unusedScheduledTaskStubLayer = Layer.succeed( - ScheduledTaskService, - ScheduledTaskService.of({ + ScheduledTaskService.ScheduledTaskService, + ScheduledTaskService.ScheduledTaskService.of({ list: () => Effect.succeed({ tasks: [] }), subscribeList: () => Stream.succeed({ tasks: [] }), upsert: () => Effect.die("ScheduledTaskService.upsert is unused in this test"), @@ -479,7 +476,7 @@ describe("orchestrator MCP toolkit", () => { const capturedTurns = yield* Ref.make>([]); const parentTerminalGates = new Map>(); const deliveryTerminalGates = new Map>(); - const registryLayer = makeProviderAdapterRegistryLayer([ + const registryLayer = ProviderAdapterRegistry.makeLayer([ makeDeterministicAdapter({ instanceId: codexInstanceId, driver: ProviderDriverKind.make("codex"), @@ -518,14 +515,17 @@ describe("orchestrator MCP toolkit", () => { ]); // Captures parent-wake offers made when a delegated child // terminalizes after the parent run settled. - const continuationOffers = yield* Ref.make>( - [], + const continuationOffers = yield* Ref.make< + ReadonlyArray + >([]); + const continuationProbeLayer = Layer.succeed( + ProviderContinuationRequests.ProviderContinuationRequests, + { + offer: (request) => + Ref.update(continuationOffers, (existing) => [...existing, request]), + take: Effect.never, + }, ); - const continuationProbeLayer = Layer.succeed(ProviderContinuationRequests, { - offer: (request) => - Ref.update(continuationOffers, (existing) => [...existing, request]), - take: Effect.never, - }); // Offers land after the finalize projection writes, so poll briefly // instead of asserting counts immediately. const waitForContinuationOffers = (count: number) => @@ -565,7 +565,7 @@ describe("orchestrator MCP toolkit", () => { ).pipe(Layer.provide(continuationProbeLayer)); const orchestrationLayer = Layer.merge( orchestratorLayer, - threadManagementServiceLayer.pipe(Layer.provide(orchestratorLayer)), + ThreadManagementService.layer.pipe(Layer.provide(orchestratorLayer)), ); const providerRegistryLayer = makeProviderRegistryLayer([ makeProviderSnapshot({ @@ -600,8 +600,8 @@ describe("orchestrator MCP toolkit", () => { // delete tools can be exercised without SQL/launch wiring. const scheduledStore = yield* Ref.make>([]); const scheduledTaskStubLayer = Layer.succeed( - ScheduledTaskService, - ScheduledTaskService.of({ + ScheduledTaskService.ScheduledTaskService, + ScheduledTaskService.ScheduledTaskService.of({ list: () => Ref.get(scheduledStore).pipe(Effect.map((tasks) => ({ tasks }))), subscribeList: () => Stream.empty, upsert: (input) => @@ -635,7 +635,7 @@ describe("orchestrator MCP toolkit", () => { ); yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const server = yield* McpServer.McpServer; yield* orchestrator.dispatch({ type: "thread.create", @@ -1063,7 +1063,7 @@ describe("orchestrator MCP toolkit", () => { streaming: false, ordinal: 999, }; - yield* (yield* EventSinkV2).write({ + yield* (yield* EventSink.EventSinkV2).write({ events: [ { id: EventId.make("event:oversized-retrieval"), @@ -3523,7 +3523,7 @@ describe("orchestrator MCP toolkit", () => { ); const orchestrationLayer = Layer.merge( orchestratorLayer, - threadManagementServiceLayer.pipe(Layer.provide(orchestratorLayer)), + ThreadManagementService.layer.pipe(Layer.provide(orchestratorLayer)), ); const providerRegistryLayer = makeProviderRegistryLayer([ makeProviderSnapshot({ @@ -3544,7 +3544,7 @@ describe("orchestrator MCP toolkit", () => { ); yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const server = yield* McpServer.McpServer; const parentCreate = yield* orchestrator.dispatch({ type: "thread.create", diff --git a/apps/server/src/mcp/ThreadMetadataMcpService.ts b/apps/server/src/mcp/ThreadMetadataMcpService.ts index 1e1bab32a218..d5f029626b12 100644 --- a/apps/server/src/mcp/ThreadMetadataMcpService.ts +++ b/apps/server/src/mcp/ThreadMetadataMcpService.ts @@ -14,10 +14,7 @@ import * as DateTime from "effect/DateTime"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; -import { - ThreadManagementError, - ThreadManagementService, -} from "../orchestration-v2/ThreadManagementService.ts"; +import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; import type { McpInvocationScope } from "./McpInvocationContext.ts"; export class ThreadMetadataMcpService extends Context.Service< @@ -38,7 +35,9 @@ function errorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); } -function threadLookupFailure(error: ThreadManagementError): OrchestratorMcpFailure { +function threadLookupFailure( + error: ThreadManagementService.ThreadManagementError, +): OrchestratorMcpFailure { return error._tag === "ThreadManagementThreadNotFoundError" ? failure("thread_not_found", error.message) : failure("orchestration_error", error.message); @@ -134,7 +133,7 @@ function resultFromThread(input: { const make = Effect.gen(function* () { const crypto = yield* Crypto.Crypto; - const threadManagement = yield* ThreadManagementService; + const threadManagement = yield* ThreadManagementService.ThreadManagementService; const update = Effect.fn("ThreadMetadataMcpService.update")(function* ( scope: McpInvocationScope, @@ -227,5 +226,5 @@ const make = Effect.gen(function* () { export const layer: Layer.Layer< ThreadMetadataMcpService, never, - Crypto.Crypto | ThreadManagementService + Crypto.Crypto | ThreadManagementService.ThreadManagementService > = Layer.effect(ThreadMetadataMcpService, make); diff --git a/apps/server/src/mcp/WorktreeMcpService.test.ts b/apps/server/src/mcp/WorktreeMcpService.test.ts index 84dd84f12b79..06e997076647 100644 --- a/apps/server/src/mcp/WorktreeMcpService.test.ts +++ b/apps/server/src/mcp/WorktreeMcpService.test.ts @@ -25,17 +25,13 @@ import { OrchestratorDispatchError, OrchestratorProjectionError, } from "../orchestration-v2/Orchestrator.ts"; -import { - ThreadManagementService, - ThreadManagementThreadArchivedError, - type ThreadManagementSendResult, -} from "../orchestration-v2/ThreadManagementService.ts"; +import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; import * as ProjectService from "../project/ProjectService.ts"; import * as ProjectSetupScriptRunner from "../project/ProjectSetupScriptRunner.ts"; import * as ServerSettings from "../serverSettings.ts"; -import { VcsStatusBroadcaster } from "../vcs/VcsStatusBroadcaster.ts"; +import * as VcsStatusBroadcaster from "../vcs/VcsStatusBroadcaster.ts"; import type * as McpInvocationContext from "./McpInvocationContext.ts"; -import { layer as worktreeMcpServiceLayer, WorktreeMcpService } from "./WorktreeMcpService.ts"; +import * as WorktreeMcpService from "./WorktreeMcpService.ts"; const environmentId = EnvironmentId.make("environment-worktree-test"); const threadId = ThreadId.make("thread-worktree-test"); @@ -177,14 +173,16 @@ const makeHarness = (options: HarnessOptions = {}) => { switch (options.continuation ?? "queued") { case "fails": return Effect.fail( - new ThreadManagementThreadArchivedError({ + new ThreadManagementService.ThreadManagementThreadArchivedError({ threadId, }), ); case "dies": return Effect.die(new Error("send defect")); default: - return Effect.succeed({ delivery: "queued" } as ThreadManagementSendResult); + return Effect.succeed({ + delivery: "queued", + } as ThreadManagementService.ThreadManagementSendResult); } }); const getById = vi.fn((id: ProjectId) => @@ -294,8 +292,8 @@ const makeHarness = (options: HarnessOptions = {}) => { const posixIsAbsolute = (value: string) => value.startsWith("/"); const serviceLayer = options.pathSemantics === undefined - ? worktreeMcpServiceLayer - : worktreeMcpServiceLayer.pipe( + ? WorktreeMcpService.layer + : WorktreeMcpService.layer.pipe( Layer.provide( Layer.succeed(Path.Path, { isAbsolute: options.pathSemantics === "win32" ? win32IsAbsolute : posixIsAbsolute, @@ -305,11 +303,11 @@ const makeHarness = (options: HarnessOptions = {}) => { const layer = serviceLayer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ dispatch, getThreadRecords, sendToThread, - } satisfies Partial), + } satisfies Partial), Layer.mock(ProjectService.ProjectService)({ getById, } satisfies Partial), @@ -329,9 +327,9 @@ const makeHarness = (options: HarnessOptions = {}) => { Layer.mock(ProjectSetupScriptRunner.ProjectSetupScriptRunner)({ runForThread, } satisfies Partial), - Layer.mock(VcsStatusBroadcaster)({ + Layer.mock(VcsStatusBroadcaster.VcsStatusBroadcaster)({ refreshStatus, - } satisfies Partial), + } satisfies Partial), NodeServices.layer, ), ), @@ -368,21 +366,21 @@ const expectTypedFailure = (exit: Exit.Exit, expected: object) // the layer's closure, so a fresh layer per call would never see it). const resolveService = (harness: ReturnType) => Effect.gen(function* () { - return yield* WorktreeMcpService; + return yield* WorktreeMcpService.WorktreeMcpService; }).pipe(Effect.provide(harness.layer)); const runHandoff = ( harness: ReturnType, - input: Parameters[1], + input: Parameters[1], ) => Effect.gen(function* () { - const service = yield* WorktreeMcpService; + const service = yield* WorktreeMcpService.WorktreeMcpService; return yield* service.handoff(harness.scope, input); }).pipe(Effect.provide(harness.layer)); const runStatus = (harness: ReturnType) => Effect.gen(function* () { - const service = yield* WorktreeMcpService; + const service = yield* WorktreeMcpService.WorktreeMcpService; return yield* service.status(harness.scope); }).pipe(Effect.provide(harness.layer)); diff --git a/apps/server/src/mcp/WorktreeMcpService.ts b/apps/server/src/mcp/WorktreeMcpService.ts index 105a9bfe2454..fb0bd85d70d3 100644 --- a/apps/server/src/mcp/WorktreeMcpService.ts +++ b/apps/server/src/mcp/WorktreeMcpService.ts @@ -18,7 +18,7 @@ import * as Option from "effect/Option"; import * as Path from "effect/Path"; import * as GitWorkflowService from "../git/GitWorkflowService.ts"; -import { ThreadManagementService } from "../orchestration-v2/ThreadManagementService.ts"; +import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; import * as ProjectService from "../project/ProjectService.ts"; import * as ProjectSetupScriptRunner from "../project/ProjectSetupScriptRunner.ts"; import * as ServerSettings from "../serverSettings.ts"; @@ -58,7 +58,7 @@ const asOperationFailed = (prefix: string) => const make = Effect.gen(function* () { const crypto = yield* Crypto.Crypto; const path = yield* Path.Path; - const threadManagement = yield* ThreadManagementService; + const threadManagement = yield* ThreadManagementService.ThreadManagementService; const projects = yield* ProjectService.ProjectService; const serverSettings = yield* ServerSettings.ServerSettingsService; const gitWorkflow = yield* GitWorkflowService.GitWorkflowService; @@ -483,7 +483,7 @@ export const layer: Layer.Layer< never, | Crypto.Crypto | Path.Path - | ThreadManagementService + | ThreadManagementService.ThreadManagementService | ProjectService.ProjectService | ServerSettings.ServerSettingsService | GitWorkflowService.GitWorkflowService diff --git a/apps/server/src/mcp/threadAccess.ts b/apps/server/src/mcp/threadAccess.ts index 0e8368b3b891..bd3a4de60010 100644 --- a/apps/server/src/mcp/threadAccess.ts +++ b/apps/server/src/mcp/threadAccess.ts @@ -10,7 +10,7 @@ import * as Effect from "effect/Effect"; import * as ThreadManagement from "../orchestration-v2/ThreadManagementService.ts"; import * as OrchestrationMcp from "./OrchestratorMcpService.ts"; -import { type McpInvocationScope, McpInvocationContext } from "./McpInvocationContext.ts"; +import * as McpInvocationContext from "./McpInvocationContext.ts"; export const unavailable = () => new OrchestratorMcpFailure({ @@ -19,7 +19,7 @@ export const unavailable = () => }); export const readCaller = Effect.fn("mcp.readCaller")(function* () { - const scope = yield* McpInvocationContext; + const scope = yield* McpInvocationContext.McpInvocationContext; if (!scope.capabilities.has("orchestration")) { return yield* new OrchestratorMcpFailure({ code: "capability_denied", @@ -42,7 +42,7 @@ function assertLiveCaller({ scope, }: { caller: OrchestrationV2ThreadShell; - scope: McpInvocationScope; + scope: McpInvocationContext.McpInvocationScope; }) { return caller.archivedAt !== null || caller.activeRunId === null || diff --git a/apps/server/src/mcp/toolkits/attachment/tools.ts b/apps/server/src/mcp/toolkits/attachment/tools.ts index 330ab8800845..55820f035c17 100644 --- a/apps/server/src/mcp/toolkits/attachment/tools.ts +++ b/apps/server/src/mcp/toolkits/attachment/tools.ts @@ -13,19 +13,19 @@ import * as Crypto from "effect/Crypto"; import * as FileSystem from "effect/FileSystem"; import * as Schema from "effect/Schema"; import { Tool, Toolkit } from "effect/unstable/ai"; -import { ServerSecretStore } from "../../../auth/ServerSecretStore.ts"; -import { ServerConfig } from "../../../config.ts"; -import { ThreadManagementService } from "../../../orchestration-v2/ThreadManagementService.ts"; -import { McpInvocationContext } from "../../McpInvocationContext.ts"; +import * as ServerSecretStore from "../../../auth/ServerSecretStore.ts"; +import * as ServerConfig from "../../../config.ts"; +import * as ThreadManagementService from "../../../orchestration-v2/ThreadManagementService.ts"; +import * as McpInvocationContext from "../../McpInvocationContext.ts"; const shared = { failure: OrchestratorMcpFailure, failureMode: "return" as const, dependencies: [ - McpInvocationContext, - ThreadManagementService, - ServerConfig, - ServerSecretStore, + McpInvocationContext.McpInvocationContext, + ThreadManagementService.ThreadManagementService, + ServerConfig.ServerConfig, + ServerSecretStore.ServerSecretStore, FileSystem.FileSystem, Crypto.Crypto, ], diff --git a/apps/server/src/mcp/toolkits/core.test.ts b/apps/server/src/mcp/toolkits/core.test.ts index 56913f5c2f31..339e5eb9b2a8 100644 --- a/apps/server/src/mcp/toolkits/core.test.ts +++ b/apps/server/src/mcp/toolkits/core.test.ts @@ -16,7 +16,7 @@ import { McpSchema, McpServer, Tool } from "effect/unstable/ai"; import { OrchestratorProjectionError } from "../../orchestration-v2/Orchestrator.ts"; import * as ThreadManagement from "../../orchestration-v2/ThreadManagementService.ts"; import * as McpHttpServer from "../McpHttpServer.ts"; -import { McpInvocationContext, type McpInvocationScope } from "../McpInvocationContext.ts"; +import * as McpInvocationContext from "../McpInvocationContext.ts"; import { OrchestratorToolkit } from "./orchestrator/tools.ts"; import { PreviewToolkit } from "./preview/tools.ts"; import { PreviewControlsToolkit } from "./previewControls/tools.ts"; @@ -76,7 +76,7 @@ it("publishes unique tool names with reference-free object-root inputs", () => { }); const threadId = ThreadId.make("mcp-core-thread"); -const scope: McpInvocationScope = { +const scope: McpInvocationContext.McpInvocationScope = { environmentId: EnvironmentId.make("mcp-core-environment"), threadId, providerSessionId: "mcp-core-session", @@ -104,7 +104,10 @@ it.effect("checks capability before accessing services through the production re const result = yield* server .callTool({ name: "t3_thread_organize", arguments: { action: "pin" } }) .pipe( - Effect.provideService(McpInvocationContext, { ...scope, capabilities: new Set() }), + Effect.provideService(McpInvocationContext.McpInvocationContext, { + ...scope, + capabilities: new Set(), + }), Effect.provideService(McpSchema.McpServerClient, client), ); expect(result.structuredContent).toMatchObject({ code: "capability_denied" }); @@ -125,7 +128,7 @@ it.effect("returns a bounded public failure without serializing storage causes", const result = yield* server .callTool({ name: "t3_thread_organize", arguments: { action: "pin" } }) .pipe( - Effect.provideService(McpInvocationContext, scope), + Effect.provideService(McpInvocationContext.McpInvocationContext, scope), Effect.provideService(McpSchema.McpServerClient, client), ); expect(result.structuredContent).toEqual({ diff --git a/apps/server/src/mcp/toolkits/device/handlers.ts b/apps/server/src/mcp/toolkits/device/handlers.ts index b6a8ed3d6323..2a8172a63411 100644 --- a/apps/server/src/mcp/toolkits/device/handlers.ts +++ b/apps/server/src/mcp/toolkits/device/handlers.ts @@ -10,7 +10,7 @@ import { import * as Effect from "effect/Effect"; import * as Path from "effect/Path"; import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; -import { ServerConfig } from "../../../config.ts"; +import * as ServerConfig from "../../../config.ts"; import { ensureAgentDeviceShim } from "../../../device/AgentDeviceShim.ts"; import { nodeRuntimeUnavailableMessage } from "@t3tools/shared/nodeRuntime"; @@ -175,7 +175,7 @@ const handlers = { (candidate) => candidate.hostId === session.hostId && candidate.id === session.deviceId, ) ?? target; const targetArgs = [...agentDeviceTargetArgs(device), ...agentArgs]; - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; const path = yield* Path.Path; const platform = yield* HostProcessPlatform; const shimDir = yield* ensureAgentDeviceShim({ diff --git a/apps/server/src/mcp/toolkits/device/tools.ts b/apps/server/src/mcp/toolkits/device/tools.ts index 58bfed41d940..c27545f86923 100644 --- a/apps/server/src/mcp/toolkits/device/tools.ts +++ b/apps/server/src/mcp/toolkits/device/tools.ts @@ -10,7 +10,7 @@ import { import * as Schema from "effect/Schema"; import * as FileSystem from "effect/FileSystem"; import * as Path from "effect/Path"; -import { ServerConfig } from "../../../config.ts"; +import * as ServerConfig from "../../../config.ts"; import { Tool, Toolkit } from "effect/unstable/ai"; import * as McpInvocationContext from "../../McpInvocationContext.ts"; @@ -51,7 +51,7 @@ const DeviceOpenTool = Tool.make("device_open", { parameters: DeviceToolOpenInput, success: DeviceToolOpenResult, failure: DeviceToolError, - dependencies: [...dependencies, FileSystem.FileSystem, Path.Path, ServerConfig], + dependencies: [...dependencies, FileSystem.FileSystem, Path.Path, ServerConfig.ServerConfig], }) .annotate(Tool.Title, "Open device") .annotate(Tool.Readonly, false) diff --git a/apps/server/src/mcp/toolkits/environment/handlers.ts b/apps/server/src/mcp/toolkits/environment/handlers.ts index 91715668b295..2c01841611dc 100644 --- a/apps/server/src/mcp/toolkits/environment/handlers.ts +++ b/apps/server/src/mcp/toolkits/environment/handlers.ts @@ -3,7 +3,7 @@ import * as Effect from "effect/Effect"; import * as Environment from "../../../environment/ServerEnvironment.ts"; import * as ThreadCommandExecutor from "../../../orchestration-v2/ThreadCommandExecutor.ts"; import * as Settings from "../../../serverSettings.ts"; -import { McpInvocationContext } from "../../McpInvocationContext.ts"; +import * as McpInvocationContext from "../../McpInvocationContext.ts"; import { readCaller, readMutationCaller, unavailable } from "../../threadAccess.ts"; import { EnvironmentToolkit } from "./tools.ts"; @@ -55,7 +55,7 @@ export const EnvironmentHandlersLive = EnvironmentToolkit.toLayer({ }), t3_environment_preferences_update: (patch) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; + const scope = yield* McpInvocationContext.McpInvocationContext; const executor = yield* ThreadCommandExecutor.ThreadCommandExecutor; return yield* executor.withLock( scope.threadId, diff --git a/apps/server/src/mcp/toolkits/environment/tools.ts b/apps/server/src/mcp/toolkits/environment/tools.ts index 03e316a56924..76fb7ea1324d 100644 --- a/apps/server/src/mcp/toolkits/environment/tools.ts +++ b/apps/server/src/mcp/toolkits/environment/tools.ts @@ -8,11 +8,11 @@ import { } from "@t3tools/contracts"; import * as Schema from "effect/Schema"; import { Tool, Toolkit } from "effect/unstable/ai"; -import { ServerEnvironment } from "../../../environment/ServerEnvironment.ts"; -import { ThreadCommandExecutor } from "../../../orchestration-v2/ThreadCommandExecutor.ts"; -import { ThreadManagementService } from "../../../orchestration-v2/ThreadManagementService.ts"; -import { ServerSettingsService } from "../../../serverSettings.ts"; -import { McpInvocationContext } from "../../McpInvocationContext.ts"; +import * as ServerEnvironment from "../../../environment/ServerEnvironment.ts"; +import * as ThreadCommandExecutor from "../../../orchestration-v2/ThreadCommandExecutor.ts"; +import * as ThreadManagementService from "../../../orchestration-v2/ThreadManagementService.ts"; +import * as Settings from "../../../serverSettings.ts"; +import * as McpInvocationContext from "../../McpInvocationContext.ts"; const PreferenceFields = { defaultThreadEnvMode: ServerSettings.fields.defaultThreadEnvMode, @@ -30,11 +30,11 @@ const shared = { failure: OrchestratorMcpFailure, failureMode: "return" as const, dependencies: [ - McpInvocationContext, - ThreadManagementService, - ServerEnvironment, - ServerSettingsService, - ThreadCommandExecutor, + McpInvocationContext.McpInvocationContext, + ThreadManagementService.ThreadManagementService, + ServerEnvironment.ServerEnvironment, + Settings.ServerSettingsService, + ThreadCommandExecutor.ThreadCommandExecutor, ], }; const EnvironmentReadTool = Tool.make("t3_environment_read", { diff --git a/apps/server/src/mcp/toolkits/orchestrator/handlers.ts b/apps/server/src/mcp/toolkits/orchestrator/handlers.ts index bdd7a0a2ffac..7dbbd21cb624 100644 --- a/apps/server/src/mcp/toolkits/orchestrator/handlers.ts +++ b/apps/server/src/mcp/toolkits/orchestrator/handlers.ts @@ -1,99 +1,99 @@ import { OrchestratorToolkit } from "./tools.ts"; import * as Effect from "effect/Effect"; -import { McpInvocationContext } from "../../McpInvocationContext.ts"; -import { OrchestratorMcpService } from "../../OrchestratorMcpService.ts"; -import { ThreadMetadataMcpService } from "../../ThreadMetadataMcpService.ts"; +import * as McpInvocationContext from "../../McpInvocationContext.ts"; +import * as OrchestratorMcpService from "../../OrchestratorMcpService.ts"; +import * as ThreadMetadataMcpService from "../../ThreadMetadataMcpService.ts"; const handlers = { orchestrator_capabilities: () => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* OrchestratorMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; return yield* service.capabilities(scope); }), delegate_task: (input) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* OrchestratorMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; return yield* service.delegateTask(scope, input); }), task_status: ({ taskId }) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* OrchestratorMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; return yield* service.taskStatus(scope, taskId); }), task_cancel: (input) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* OrchestratorMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; return yield* service.cancelTask(scope, input); }), schedule_task: (input) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* OrchestratorMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; return yield* service.scheduleTask(scope, input); }), list_scheduled_tasks: () => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* OrchestratorMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; return yield* service.listScheduledTasks(scope); }), update_scheduled_task: (input) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* OrchestratorMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; return yield* service.updateScheduledTask(scope, input); }), delete_scheduled_task: (input) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* OrchestratorMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; return yield* service.deleteScheduledTask(scope, input); }), create_threads: (input) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* OrchestratorMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; return yield* service.createThreads(scope, input); }), t3_thread_list: (input) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* OrchestratorMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; return yield* service.listThreads(scope, input); }), t3_thread_read: (input) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* OrchestratorMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; return yield* service.readThread(scope, input); }), t3_thread_update: (input) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* ThreadMetadataMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* ThreadMetadataMcpService.ThreadMetadataMcpService; return yield* service.update(scope, input); }), t3_thread_send: (input) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* OrchestratorMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; return yield* service.sendToThread(scope, input); }), t3_thread_wait: (input) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* OrchestratorMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; return yield* service.waitForThread(scope, input); }), t3_thread_interrupt: (input) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* OrchestratorMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* OrchestratorMcpService.OrchestratorMcpService; return yield* service.interruptThread(scope, input); }), } satisfies Parameters[0]; diff --git a/apps/server/src/mcp/toolkits/orchestrator/tools.ts b/apps/server/src/mcp/toolkits/orchestrator/tools.ts index 2f169c611de3..833e88bd2ae8 100644 --- a/apps/server/src/mcp/toolkits/orchestrator/tools.ts +++ b/apps/server/src/mcp/toolkits/orchestrator/tools.ts @@ -30,13 +30,16 @@ import { import { Tool, Toolkit } from "effect/unstable/ai"; import * as McpInvocationContext from "../../McpInvocationContext.ts"; -import { OrchestratorMcpService } from "../../OrchestratorMcpService.ts"; -import { ThreadMetadataMcpService } from "../../ThreadMetadataMcpService.ts"; +import * as OrchestratorMcpService from "../../OrchestratorMcpService.ts"; +import * as ThreadMetadataMcpService from "../../ThreadMetadataMcpService.ts"; -const dependencies = [McpInvocationContext.McpInvocationContext, OrchestratorMcpService]; +const dependencies = [ + McpInvocationContext.McpInvocationContext, + OrchestratorMcpService.OrchestratorMcpService, +]; const threadMetadataDependencies = [ McpInvocationContext.McpInvocationContext, - ThreadMetadataMcpService, + ThreadMetadataMcpService.ThreadMetadataMcpService, ]; const OrchestratorCapabilitiesTool = Tool.make("orchestrator_capabilities", { diff --git a/apps/server/src/mcp/toolkits/previewControls/tools.ts b/apps/server/src/mcp/toolkits/previewControls/tools.ts index aa0ed1378433..c9870a658584 100644 --- a/apps/server/src/mcp/toolkits/previewControls/tools.ts +++ b/apps/server/src/mcp/toolkits/previewControls/tools.ts @@ -7,13 +7,13 @@ import { } from "@t3tools/contracts"; import * as Schema from "effect/Schema"; import { Tool, Toolkit } from "effect/unstable/ai"; -import { PreviewManager } from "../../../preview/Manager.ts"; -import { McpInvocationContext } from "../../McpInvocationContext.ts"; +import * as PreviewManager from "../../../preview/Manager.ts"; +import * as McpInvocationContext from "../../McpInvocationContext.ts"; const shared = { failure: Schema.Union([OrchestratorMcpFailure, PreviewAutomationUnavailableError]), failureMode: "return" as const, - dependencies: [McpInvocationContext, PreviewManager], + dependencies: [McpInvocationContext.McpInvocationContext, PreviewManager.PreviewManager], }; const PreviewListTool = Tool.make("t3_preview_list", { ...shared, diff --git a/apps/server/src/mcp/toolkits/project/handlers.test.ts b/apps/server/src/mcp/toolkits/project/handlers.test.ts index 810e52af2163..68590c8b3429 100644 --- a/apps/server/src/mcp/toolkits/project/handlers.test.ts +++ b/apps/server/src/mcp/toolkits/project/handlers.test.ts @@ -16,7 +16,7 @@ import * as ThreadLaunch from "../../../orchestration-v2/ThreadLaunchService.ts" import * as ThreadManagement from "../../../orchestration-v2/ThreadManagementService.ts"; import * as ServerConfig from "../../../config.ts"; import * as Project from "../../../project/ProjectService.ts"; -import { McpInvocationContext } from "../../McpInvocationContext.ts"; +import * as McpInvocationContext from "../../McpInvocationContext.ts"; import { ProjectHandlersLive } from "./handlers.ts"; import { ProjectToolkit } from "./tools.ts"; @@ -40,7 +40,7 @@ it.effect("attributes a launched thread's first message to the calling thread", let launchedSender: ThreadId | undefined; const dependencies = Layer.mergeAll( NodeCrypto.layer, - Layer.succeed(McpInvocationContext, { + Layer.succeed(McpInvocationContext.McpInvocationContext, { environmentId: EnvironmentId.make("environment"), threadId: sourceThreadId, providerSessionId: "session", diff --git a/apps/server/src/mcp/toolkits/project/tools.ts b/apps/server/src/mcp/toolkits/project/tools.ts index c4dd9d9403d7..938dfb277c83 100644 --- a/apps/server/src/mcp/toolkits/project/tools.ts +++ b/apps/server/src/mcp/toolkits/project/tools.ts @@ -18,21 +18,26 @@ import { SourceControlCloneRepositoryResult, } from "@t3tools/contracts"; import * as FileSystem from "effect/FileSystem"; -import { ServerConfig } from "../../../config.ts"; -import { ThreadLaunchService } from "../../../orchestration-v2/ThreadLaunchService.ts"; +import * as ServerConfig from "../../../config.ts"; +import * as ThreadLaunchService from "../../../orchestration-v2/ThreadLaunchService.ts"; import * as Crypto from "effect/Crypto"; import * as Schema from "effect/Schema"; import { Tool, Toolkit } from "effect/unstable/ai"; -import { ProjectService } from "../../../project/ProjectService.ts"; -import { ThreadManagementService } from "../../../orchestration-v2/ThreadManagementService.ts"; -import { SourceControlRepositoryService } from "../../../sourceControl/SourceControlRepositoryService.ts"; -import { McpInvocationContext } from "../../McpInvocationContext.ts"; +import * as ProjectService from "../../../project/ProjectService.ts"; +import * as ThreadManagementService from "../../../orchestration-v2/ThreadManagementService.ts"; +import * as SourceControlRepositoryService from "../../../sourceControl/SourceControlRepositoryService.ts"; +import * as McpInvocationContext from "../../McpInvocationContext.ts"; const shared = { success: Project, failure: OrchestratorMcpFailure, failureMode: "return" as const, - dependencies: [McpInvocationContext, ThreadManagementService, ProjectService, Crypto.Crypto], + dependencies: [ + McpInvocationContext.McpInvocationContext, + ThreadManagementService.ThreadManagementService, + ProjectService.ProjectService, + Crypto.Crypto, + ], }; const ProjectListTool = Tool.make("t3_project_list", { ...shared, @@ -81,7 +86,10 @@ const ProjectCloneTool = Tool.make("t3_project_clone", { "Clone a repository using the app's source-control service. This only clones; register the returned cwd with t3_project_create. An existing destination is not adopted or removed on failure.", parameters: SourceControlCloneRepositoryInput, success: SourceControlCloneRepositoryResult, - dependencies: [...shared.dependencies, SourceControlRepositoryService], + dependencies: [ + ...shared.dependencies, + SourceControlRepositoryService.SourceControlRepositoryService, + ], }) .annotate(Tool.Destructive, true) .annotate(Tool.OpenWorld, true); @@ -116,7 +124,12 @@ const ThreadLaunchTool = Tool.make("t3_thread_launch", { runId: Schema.NullOr(RunId), status: Schema.NullOr(OrchestrationV2RunStatus), }), - dependencies: [...shared.dependencies, ThreadLaunchService, FileSystem.FileSystem, ServerConfig], + dependencies: [ + ...shared.dependencies, + ThreadLaunchService.ThreadLaunchService, + FileSystem.FileSystem, + ServerConfig.ServerConfig, + ], }) .annotate(Tool.Destructive, true) .annotate(Tool.OpenWorld, true); diff --git a/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts b/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts index c45cc7dacb60..dab6e7176498 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts @@ -16,16 +16,12 @@ import * as Ref from "effect/Ref"; import * as Stream from "effect/Stream"; import type { Tool } from "effect/unstable/ai"; -import { - OrchestratorV2, - type OrchestratorV2Shape, - OrchestratorDispatchError, -} from "../../../orchestration-v2/Orchestrator.ts"; +import * as Orchestrator from "../../../orchestration-v2/Orchestrator.ts"; import { type PullRequestTestThread, v2PullRequestThread, } from "../../../orchestration-v2/testkit/pullRequestFixtures.ts"; -import { ProjectService } from "../../../project/ProjectService.ts"; +import * as ProjectService from "../../../project/ProjectService.ts"; import * as McpInvocationContext from "../../McpInvocationContext.ts"; import { listThreadPullRequests, PullRequestsToolkitHandlersLive } from "./handlers.ts"; import { PullRequestLinkFailedError, PullRequestsToolkit } from "./tools.ts"; @@ -140,11 +136,11 @@ const makeHarness = Effect.fn("makePullRequestsToolkitHarness")(function* ( const commands = yield* Ref.make>([]); const thread = options.thread === undefined ? makeThread([]) : options.thread; const project = options.project === undefined ? makeProject() : options.project; - const dispatch: OrchestratorV2Shape["dispatch"] = (command) => + const dispatch: Orchestrator.OrchestratorV2Shape["dispatch"] = (command) => Effect.gen(function* () { const rejection = options.reject?.(command) ?? null; if (rejection !== null) - return yield* new OrchestratorDispatchError({ + return yield* new Orchestrator.OrchestratorDispatchError({ commandId: command.commandId, commandType: command.type, cause: rejection, @@ -153,10 +149,10 @@ const makeHarness = Effect.fn("makePullRequestsToolkitHarness")(function* ( return { sequence: 1, storedEvents: [] }; }); const dependencies = Layer.mergeAll( - Layer.mock(ProjectService)({ + Layer.mock(ProjectService.ProjectService)({ getShell: () => Effect.succeed(Option.fromNullishOr(project)), }), - Layer.mock(OrchestratorV2)({ + Layer.mock(Orchestrator.OrchestratorV2)({ getThreadShell: (id) => Effect.succeed(id === THREAD_ID && thread ? v2PullRequestThread(thread) : null), dispatch, diff --git a/apps/server/src/mcp/toolkits/pullRequests/handlers.ts b/apps/server/src/mcp/toolkits/pullRequests/handlers.ts index f45b3198d515..19677e1bd212 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/handlers.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/handlers.ts @@ -23,7 +23,7 @@ import * as Crypto from "effect/Crypto"; import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; -import { OrchestratorV2 } from "../../../orchestration-v2/Orchestrator.ts"; +import * as Orchestrator from "../../../orchestration-v2/Orchestrator.ts"; import * as ProjectService from "../../../project/ProjectService.ts"; import * as McpInvocationContext from "../../McpInvocationContext.ts"; import { @@ -148,7 +148,7 @@ export function listThreadPullRequests( } const make = Effect.gen(function* () { - const engine = yield* OrchestratorV2; + const engine = yield* Orchestrator.OrchestratorV2; const projects = yield* ProjectService.ProjectService; const crypto = yield* Crypto.Crypto; diff --git a/apps/server/src/mcp/toolkits/pullRequests/tools.ts b/apps/server/src/mcp/toolkits/pullRequests/tools.ts index 5f3f1fcae3be..90f22cea2615 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/tools.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/tools.ts @@ -10,12 +10,12 @@ import * as Tool from "effect/unstable/ai/Tool"; import * as Toolkit from "effect/unstable/ai/Toolkit"; import * as McpInvocationContext from "../../McpInvocationContext.ts"; -import { OrchestratorV2 } from "../../../orchestration-v2/Orchestrator.ts"; +import * as Orchestrator from "../../../orchestration-v2/Orchestrator.ts"; import * as ProjectService from "../../../project/ProjectService.ts"; const dependencies = [ McpInvocationContext.McpInvocationContext, - OrchestratorV2, + Orchestrator.OrchestratorV2, ProjectService.ProjectService, ]; diff --git a/apps/server/src/mcp/toolkits/thread/tools.ts b/apps/server/src/mcp/toolkits/thread/tools.ts index 4700db82a1d3..1517763b936e 100644 --- a/apps/server/src/mcp/toolkits/thread/tools.ts +++ b/apps/server/src/mcp/toolkits/thread/tools.ts @@ -23,9 +23,9 @@ import * as Schema from "effect/Schema"; import { Tool, Toolkit } from "effect/unstable/ai"; import * as ThreadSearch from "../../../orchestration-v2/ThreadSearch.ts"; -import { ScheduledTaskService } from "../../../scheduledTasks/ScheduledTaskService.ts"; -import { ThreadManagementService } from "../../../orchestration-v2/ThreadManagementService.ts"; -import { McpInvocationContext } from "../../McpInvocationContext.ts"; +import * as ScheduledTaskService from "../../../scheduledTasks/ScheduledTaskService.ts"; +import * as ThreadManagementService from "../../../orchestration-v2/ThreadManagementService.ts"; +import * as McpInvocationContext from "../../McpInvocationContext.ts"; const ThreadOrganizeTool = Tool.make("t3_thread_organize", { description: @@ -48,7 +48,11 @@ const ThreadOrganizeTool = Tool.make("t3_thread_organize", { success: OrchestrationV2DispatchCommandResult, failure: OrchestratorMcpFailure, failureMode: "return" as const, - dependencies: [McpInvocationContext, ThreadManagementService, Crypto.Crypto], + dependencies: [ + McpInvocationContext.McpInvocationContext, + ThreadManagementService.ThreadManagementService, + Crypto.Crypto, + ], }) .annotate(Tool.Title, "Organize a thread") .annotate(Tool.Destructive, true); @@ -58,7 +62,11 @@ const commandTool = { success: OrchestrationV2DispatchCommandResult, failure: OrchestratorMcpFailure, failureMode: "return" as const, - dependencies: [McpInvocationContext, ThreadManagementService, Crypto.Crypto], + dependencies: [ + McpInvocationContext.McpInvocationContext, + ThreadManagementService.ThreadManagementService, + Crypto.Crypto, + ], }; const queueEntry = Schema.Struct({ queuedRunId: RunId, @@ -245,7 +253,7 @@ const ScheduledTaskRunTool = Tool.make("run_scheduled_task_now", { runCount: NonNegativeInt, nextRunAt: ScheduledTask.fields.nextRunAt, }), - dependencies: [...commandTool.dependencies, ScheduledTaskService], + dependencies: [...commandTool.dependencies, ScheduledTaskService.ScheduledTaskService], }) .annotate(Tool.Destructive, true) .annotate(Tool.OpenWorld, true); diff --git a/apps/server/src/mcp/toolkits/worktree/handlers.ts b/apps/server/src/mcp/toolkits/worktree/handlers.ts index abb03258de91..0ecabdaecb97 100644 --- a/apps/server/src/mcp/toolkits/worktree/handlers.ts +++ b/apps/server/src/mcp/toolkits/worktree/handlers.ts @@ -5,14 +5,14 @@ import * as Project from "../../../project/ProjectService.ts"; import { readCaller, unavailable } from "../../threadAccess.ts"; import * as Effect from "effect/Effect"; -import { McpInvocationContext } from "../../McpInvocationContext.ts"; -import { WorktreeMcpService } from "../../WorktreeMcpService.ts"; +import * as McpInvocationContext from "../../McpInvocationContext.ts"; +import * as WorktreeMcpService from "../../WorktreeMcpService.ts"; import { WorktreeToolkit } from "./tools.ts"; const handlers = { t3_worktree_list: (input) => Effect.gen(function* () { - const context = yield* McpInvocationContext; + const context = yield* McpInvocationContext.McpInvocationContext; if (!context.capabilities.has("worktree")) return yield* new OrchestratorMcpFailure({ code: "capability_denied", @@ -33,14 +33,14 @@ const handlers = { }), t3_worktree_handoff: (input) => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* WorktreeMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* WorktreeMcpService.WorktreeMcpService; return yield* service.handoff(scope, input); }), t3_worktree_status: () => Effect.gen(function* () { - const scope = yield* McpInvocationContext; - const service = yield* WorktreeMcpService; + const scope = yield* McpInvocationContext.McpInvocationContext; + const service = yield* WorktreeMcpService.WorktreeMcpService; return yield* service.status(scope); }), } satisfies Parameters[0]; diff --git a/apps/server/src/mcp/toolkits/worktree/registration.test.ts b/apps/server/src/mcp/toolkits/worktree/registration.test.ts index e68f3e355516..c9d6955a634d 100644 --- a/apps/server/src/mcp/toolkits/worktree/registration.test.ts +++ b/apps/server/src/mcp/toolkits/worktree/registration.test.ts @@ -1,6 +1,6 @@ -import { OrchestratorV2 } from "../../../orchestration-v2/Orchestrator.ts"; -import { ProjectionStoreV2 } from "../../../orchestration-v2/ProjectionStore.ts"; -import { DeviceService } from "../../../device/DeviceService.ts"; +import * as Orchestrator from "../../../orchestration-v2/Orchestrator.ts"; +import * as ProjectionStore from "../../../orchestration-v2/ProjectionStore.ts"; +import * as DeviceService from "../../../device/DeviceService.ts"; import * as ServerConfig from "../../../config.ts"; import { expect, it } from "@effect/vitest"; import { NodeHttpServer } from "@effect/platform-node"; @@ -13,31 +13,31 @@ import { HttpBody, HttpClient, HttpRouter } from "effect/unstable/http"; import * as ServerEnvironment from "../../../environment/ServerEnvironment.ts"; import * as GitWorkflowService from "../../../git/GitWorkflowService.ts"; -import { ProviderAdapterRegistryV2 } from "../../../orchestration-v2/ProviderAdapterRegistry.ts"; -import { ThreadManagementService } from "../../../orchestration-v2/ThreadManagementService.ts"; +import * as ProviderAdapterRegistry from "../../../orchestration-v2/ProviderAdapterRegistry.ts"; +import * as ThreadManagementService from "../../../orchestration-v2/ThreadManagementService.ts"; import * as ProjectService from "../../../project/ProjectService.ts"; import * as ProjectSetupScriptRunner from "../../../project/ProjectSetupScriptRunner.ts"; -import { ProviderRegistry } from "../../../provider/Services/ProviderRegistry.ts"; -import { ScheduledTaskService } from "../../../scheduledTasks/ScheduledTaskService.ts"; +import * as ProviderRegistry from "../../../provider/Services/ProviderRegistry.ts"; +import * as ScheduledTaskService from "../../../scheduledTasks/ScheduledTaskService.ts"; import * as ServerSettings from "../../../serverSettings.ts"; -import { VcsStatusBroadcaster } from "../../../vcs/VcsStatusBroadcaster.ts"; +import * as VcsStatusBroadcaster from "../../../vcs/VcsStatusBroadcaster.ts"; import * as McpHttpServer from "../../McpHttpServer.ts"; import * as McpSessionRegistry from "../../McpSessionRegistry.ts"; import * as PreviewAutomationBroker from "../../PreviewAutomationBroker.ts"; const StubServicesLive = Layer.mergeAll( - Layer.mock(OrchestratorV2)({}), - Layer.mock(ProjectionStoreV2)({}), - Layer.mock(DeviceService)({}), - Layer.mock(ThreadManagementService)({}), - Layer.mock(ProviderRegistry)({}), - Layer.mock(ProviderAdapterRegistryV2)({}), - Layer.mock(ScheduledTaskService)({}), + Layer.mock(Orchestrator.OrchestratorV2)({}), + Layer.mock(ProjectionStore.ProjectionStoreV2)({}), + Layer.mock(DeviceService.DeviceService)({}), + Layer.mock(ThreadManagementService.ThreadManagementService)({}), + Layer.mock(ProviderRegistry.ProviderRegistry)({}), + Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({}), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({}), Layer.mock(ProjectService.ProjectService)({}), ServerSettings.layerTest({}), Layer.mock(GitWorkflowService.GitWorkflowService)({}), Layer.mock(ProjectSetupScriptRunner.ProjectSetupScriptRunner)({}), - Layer.mock(VcsStatusBroadcaster)({}), + Layer.mock(VcsStatusBroadcaster.VcsStatusBroadcaster)({}), ); const ToolsListPayload = Schema.fromJsonString( diff --git a/apps/server/src/mcp/toolkits/worktree/tools.ts b/apps/server/src/mcp/toolkits/worktree/tools.ts index e67ff5176a9a..d4cff97dbf75 100644 --- a/apps/server/src/mcp/toolkits/worktree/tools.ts +++ b/apps/server/src/mcp/toolkits/worktree/tools.ts @@ -8,15 +8,18 @@ import { WorktreeMcpStatusResult, } from "@t3tools/contracts"; import * as Schema from "effect/Schema"; -import { GitWorkflowService } from "../../../git/GitWorkflowService.ts"; -import { ProjectService } from "../../../project/ProjectService.ts"; -import { ThreadManagementService } from "../../../orchestration-v2/ThreadManagementService.ts"; +import * as GitWorkflowService from "../../../git/GitWorkflowService.ts"; +import * as ProjectService from "../../../project/ProjectService.ts"; +import * as ThreadManagementService from "../../../orchestration-v2/ThreadManagementService.ts"; import { Tool, Toolkit } from "effect/unstable/ai"; import * as McpInvocationContext from "../../McpInvocationContext.ts"; -import { WorktreeMcpService } from "../../WorktreeMcpService.ts"; +import * as WorktreeMcpService from "../../WorktreeMcpService.ts"; -const dependencies = [McpInvocationContext.McpInvocationContext, WorktreeMcpService]; +const dependencies = [ + McpInvocationContext.McpInvocationContext, + WorktreeMcpService.WorktreeMcpService, +]; const WorktreeHandoffTool = Tool.make("t3_worktree_handoff", { description: @@ -66,9 +69,9 @@ const WorktreeListTool = Tool.make("t3_worktree_list", { failureMode: "return", dependencies: [ McpInvocationContext.McpInvocationContext, - ThreadManagementService, - ProjectService, - GitWorkflowService, + ThreadManagementService.ThreadManagementService, + ProjectService.ProjectService, + GitWorkflowService.GitWorkflowService, ], }) .annotate(Tool.Readonly, true) diff --git a/apps/server/src/observability/EventLoopMonitor.test.ts b/apps/server/src/observability/EventLoopMonitor.test.ts index fbe30157aec2..1b9fd72049b9 100644 --- a/apps/server/src/observability/EventLoopMonitor.test.ts +++ b/apps/server/src/observability/EventLoopMonitor.test.ts @@ -4,12 +4,12 @@ import * as Layer from "effect/Layer"; import * as Tracer from "effect/Tracer"; import * as TestClock from "effect/testing/TestClock"; -import { type EventLoopReadings, layerWith, stallMs } from "./EventLoopMonitor.ts"; +import * as EventLoopMonitor from "./EventLoopMonitor.ts"; const ms = (value: number) => value * 1e6; // Node reports a stall of S as a gap of up to S + 1 s, the histogram resolution. -const stalled: EventLoopReadings = { +const stalled: EventLoopMonitor.EventLoopReadings = { delayMaxNs: ms(5_950), activeMs: 6_200, utilization: 0.176, @@ -23,7 +23,7 @@ const stalled: EventLoopReadings = { rssBytes: 1536 * 1024 * 1024, }; // Over the threshold as read, but not once the resolution is subtracted. -const quiet: EventLoopReadings = { ...stalled, delayMaxNs: ms(2_950) }; +const quiet: EventLoopMonitor.EventLoopReadings = { ...stalled, delayMaxNs: ms(2_950) }; describe("EventLoopMonitor", () => { it.effect("records a warning span only for samples that saw a stall", () => @@ -40,7 +40,9 @@ describe("EventLoopMonitor", () => { const samples = [stalled, quiet, stalled]; yield* Effect.gen(function* () { - yield* Layer.build(layerWith(Effect.succeed(Effect.sync(() => samples.shift() ?? quiet)))); + yield* Layer.build( + EventLoopMonitor.layerWith(Effect.succeed(Effect.sync(() => samples.shift() ?? quiet))), + ); yield* TestClock.adjust("60 seconds"); assert.lengthOf(spans, 0); yield* TestClock.adjust("30 seconds"); @@ -70,8 +72,12 @@ describe("EventLoopMonitor", () => { it("ignores delay the loop spent idle, such as a system sleep", () => { // Waking from sleep reads as a long gap, but the loop was idle in poll for it. - const asleep: EventLoopReadings = { ...stalled, delayMaxNs: ms(600_000), activeMs: 900 }; - assert.isUndefined(stallMs(asleep)); - assert.strictEqual(stallMs({ ...asleep, activeMs: 600_000 }), 599_000); + const asleep: EventLoopMonitor.EventLoopReadings = { + ...stalled, + delayMaxNs: ms(600_000), + activeMs: 900, + }; + assert.isUndefined(EventLoopMonitor.stallMs(asleep)); + assert.strictEqual(EventLoopMonitor.stallMs({ ...asleep, activeMs: 600_000 }), 599_000); }); }); diff --git a/apps/server/src/persistence/Layers/OrchestrationCommandReceipts.ts b/apps/server/src/persistence/Layers/OrchestrationCommandReceipts.ts index 33ebeb8a2b18..4302b9004f94 100644 --- a/apps/server/src/persistence/Layers/OrchestrationCommandReceipts.ts +++ b/apps/server/src/persistence/Layers/OrchestrationCommandReceipts.ts @@ -5,18 +5,13 @@ import * as Layer from "effect/Layer"; import { toPersistenceSqlError } from "../Errors.ts"; -import { - GetByCommandIdInput, - OrchestrationCommandReceipt, - OrchestrationCommandReceiptRepository, - type OrchestrationCommandReceiptRepositoryShape, -} from "../Services/OrchestrationCommandReceipts.ts"; +import * as OrchestrationCommandReceipts from "../Services/OrchestrationCommandReceipts.ts"; const makeOrchestrationCommandReceiptRepository = Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; const upsertReceiptRow = SqlSchema.void({ - Request: OrchestrationCommandReceipt, + Request: OrchestrationCommandReceipts.OrchestrationCommandReceipt, execute: (receipt) => sql` INSERT INTO orchestration_command_receipts ( @@ -52,8 +47,8 @@ const makeOrchestrationCommandReceiptRepository = Effect.gen(function* () { }); const findReceiptByCommandId = SqlSchema.findOneOption({ - Request: GetByCommandIdInput, - Result: OrchestrationCommandReceipt, + Request: OrchestrationCommandReceipts.GetByCommandIdInput, + Result: OrchestrationCommandReceipts.OrchestrationCommandReceipt, execute: ({ commandId }) => sql` SELECT @@ -70,13 +65,17 @@ const makeOrchestrationCommandReceiptRepository = Effect.gen(function* () { `, }); - const upsert: OrchestrationCommandReceiptRepositoryShape["upsert"] = (receipt) => - upsertReceiptRow(receipt).pipe( - Effect.mapError(toPersistenceSqlError("OrchestrationCommandReceiptRepository.upsert:query")), - ); + const upsert: OrchestrationCommandReceipts.OrchestrationCommandReceiptRepositoryShape["upsert"] = + (receipt) => + upsertReceiptRow(receipt).pipe( + Effect.mapError( + toPersistenceSqlError("OrchestrationCommandReceiptRepository.upsert:query"), + ), + ); - const insertIfAbsent: OrchestrationCommandReceiptRepositoryShape["insertIfAbsent"] = (receipt) => - sql<{ readonly command_id: string }>` + const insertIfAbsent: OrchestrationCommandReceipts.OrchestrationCommandReceiptRepositoryShape["insertIfAbsent"] = + (receipt) => + sql<{ readonly command_id: string }>` INSERT INTO orchestration_command_receipts ( command_id, aggregate_kind, @@ -100,27 +99,28 @@ const makeOrchestrationCommandReceiptRepository = Effect.gen(function* () { ON CONFLICT(command_id) DO NOTHING RETURNING command_id `.pipe( - Effect.map((rows) => rows.length === 1), - Effect.mapError( - toPersistenceSqlError("OrchestrationCommandReceiptRepository.insertIfAbsent:query"), - ), - ); + Effect.map((rows) => rows.length === 1), + Effect.mapError( + toPersistenceSqlError("OrchestrationCommandReceiptRepository.insertIfAbsent:query"), + ), + ); - const getByCommandId: OrchestrationCommandReceiptRepositoryShape["getByCommandId"] = (input) => - findReceiptByCommandId(input).pipe( - Effect.mapError( - toPersistenceSqlError("OrchestrationCommandReceiptRepository.getByCommandId:query"), - ), - ); + const getByCommandId: OrchestrationCommandReceipts.OrchestrationCommandReceiptRepositoryShape["getByCommandId"] = + (input) => + findReceiptByCommandId(input).pipe( + Effect.mapError( + toPersistenceSqlError("OrchestrationCommandReceiptRepository.getByCommandId:query"), + ), + ); return { insertIfAbsent, upsert, getByCommandId, - } satisfies OrchestrationCommandReceiptRepositoryShape; + } satisfies OrchestrationCommandReceipts.OrchestrationCommandReceiptRepositoryShape; }); export const OrchestrationCommandReceiptRepositoryLive = Layer.effect( - OrchestrationCommandReceiptRepository, + OrchestrationCommandReceipts.OrchestrationCommandReceiptRepository, makeOrchestrationCommandReceiptRepository, ); diff --git a/apps/server/src/persistence/Layers/OrchestrationEventStore.sequence.test.ts b/apps/server/src/persistence/Layers/OrchestrationEventStore.sequence.test.ts index 2eccb97e4e97..c5d630610b8c 100644 --- a/apps/server/src/persistence/Layers/OrchestrationEventStore.sequence.test.ts +++ b/apps/server/src/persistence/Layers/OrchestrationEventStore.sequence.test.ts @@ -10,7 +10,7 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; import { runMigrations } from "../Migrations.ts"; import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient"; -import { OrchestrationEventStore } from "../Services/OrchestrationEventStore.ts"; +import * as OrchestrationEventStore from "../Services/OrchestrationEventStore.ts"; import { OrchestrationEventStoreLive } from "./OrchestrationEventStore.ts"; import { SqlitePersistenceMemory } from "./Sqlite.ts"; @@ -74,7 +74,7 @@ const seedEvents = Effect.fn("test.seedSequenceEvents")(function* ( it.effect("keeps application and scoped agent high-water marks separate from legacy history", () => Effect.gen(function* () { - const store = yield* OrchestrationEventStore; + const store = yield* OrchestrationEventStore.OrchestrationEventStore; const sql = yield* SqlClient.SqlClient; const target = ThreadId.make("target"); assert.equal(yield* store.latestApplicationSequence, 0); @@ -119,7 +119,7 @@ it.effect( "preserves mixed application paging, scoped replay, and the catch-up to live boundary", () => Effect.gen(function* () { - const store = yield* OrchestrationEventStore; + const store = yield* OrchestrationEventStore.OrchestrationEventStore; const rows = yield* seedEvents( Array.from({ length: 1_560 }, (_, index) => { const kind = index % 3 === 1 ? "project" : "thread"; @@ -211,7 +211,7 @@ it.effect( it.effect("uses indexed high-water lookups for populated history without OR scans", () => Effect.gen(function* () { - const store = yield* OrchestrationEventStore; + const store = yield* OrchestrationEventStore.OrchestrationEventStore; const sql = yield* SqlClient.SqlClient; yield* runMigrations(); yield* sql` diff --git a/apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts b/apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts index 59d3af4248b8..b053c2403401 100644 --- a/apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts +++ b/apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts @@ -26,7 +26,7 @@ import { } from "../../orchestration-v2/LiveStreamBudget.ts"; import { PersistenceDecodeError } from "../Errors.ts"; import { toShellApplicationEvent } from "../../orchestration-v2/ShellStream.ts"; -import { OrchestrationEventStore } from "../Services/OrchestrationEventStore.ts"; +import * as OrchestrationEventStore from "../Services/OrchestrationEventStore.ts"; import { OrchestrationEventStoreLive } from "./OrchestrationEventStore.ts"; import { SqlitePersistenceMemory } from "./Sqlite.ts"; const isPersistenceDecodeError = Schema.is(PersistenceDecodeError); @@ -39,7 +39,7 @@ layer("OrchestrationEventStore", (it) => { it.effect("retains only shell metadata from oversized replay and live application events", () => Effect.scoped( Effect.gen(function* () { - const store = yield* OrchestrationEventStore; + const store = yield* OrchestrationEventStore.OrchestrationEventStore; const afterSequence = yield* store.latestApplicationSequence; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:large-shell-body"); @@ -104,7 +104,7 @@ layer("OrchestrationEventStore", (it) => { it.effect("stores json columns as strings and replays CLI-origin events", () => Effect.gen(function* () { - const eventStore = yield* OrchestrationEventStore; + const eventStore = yield* OrchestrationEventStore.OrchestrationEventStore; const sql = yield* SqlClient.SqlClient; const now = "2026-01-01T00:00:00.000Z"; @@ -170,7 +170,7 @@ layer("OrchestrationEventStore", (it) => { it.effect("fails with PersistenceDecodeError when stored json is invalid", () => Effect.gen(function* () { - const eventStore = yield* OrchestrationEventStore; + const eventStore = yield* OrchestrationEventStore.OrchestrationEventStore; const sql = yield* SqlClient.SqlClient; const now = "2026-01-01T00:00:00.000Z"; @@ -222,7 +222,7 @@ layer("OrchestrationEventStore", (it) => { it.effect("orders project and V2 agent events in the retained application event source", () => Effect.gen(function* () { - const eventStore = yield* OrchestrationEventStore; + const eventStore = yield* OrchestrationEventStore.OrchestrationEventStore; const projectId = ProjectId.make("project-shared-stream"); const threadId = ThreadId.make("thread-shared-stream"); const providerInstanceId = ProviderInstanceId.make("codex"); @@ -321,7 +321,7 @@ layer("OrchestrationEventStore", (it) => { ); it.effect("measures only a bounded thread replay before decoding its payloads", () => Effect.gen(function* () { - const store = yield* OrchestrationEventStore; + const store = yield* OrchestrationEventStore.OrchestrationEventStore; const sql = yield* SqlClient.SqlClient; const threadId = ThreadId.make("measured-replay-thread"); const baseline = yield* store.latestApplicationSequence; @@ -398,7 +398,7 @@ for (const phase of ["high-water", "replay"] as const) { it.effect(`bounds application live events while the ${phase} query is blocked`, () => Effect.scoped( Effect.gen(function* () { - const store = yield* OrchestrationEventStore; + const store = yield* OrchestrationEventStore.OrchestrationEventStore; const now = "2026-01-03T00:00:00.000Z"; const projectId = ProjectId.make(`project:blocked-${phase}`); const projectEvent = yield* store.appendProjectEvent({ @@ -469,7 +469,7 @@ for (const phase of ["high-water", "replay"] as const) { it.effect("releases consumed application replay pages", () => Effect.gen(function* () { - const store = yield* OrchestrationEventStore; + const store = yield* OrchestrationEventStore.OrchestrationEventStore; const afterSequence = yield* store.latestApplicationSequence; yield* Effect.forEach( Array.from({ length: 1501 }, (_, index) => index), diff --git a/apps/server/src/persistence/Layers/OrchestrationEventStore.ts b/apps/server/src/persistence/Layers/OrchestrationEventStore.ts index 874e82888490..f947d2b74c66 100644 --- a/apps/server/src/persistence/Layers/OrchestrationEventStore.ts +++ b/apps/server/src/persistence/Layers/OrchestrationEventStore.ts @@ -29,11 +29,7 @@ import { toPersistenceSqlError, type OrchestrationEventStoreError, } from "../Errors.ts"; -import { - OrchestrationEventStore, - type OrchestrationEventStoreShape, - type UnsequencedProjectEvent, -} from "../Services/OrchestrationEventStore.ts"; +import * as OrchestrationEventStore from "../Services/OrchestrationEventStore.ts"; const encodeProjectIcon = Schema.encodeSync(ProjectIconOverride); const decodeProjectEvent = Schema.decodeUnknownEffect(ApplicationProjectEvent); @@ -160,7 +156,9 @@ function rowToApplicationStoredEvent( return row.aggregate_kind === "project" ? rowToProjectEvent(row) : rowToV2StoredEvent(row); } -function inferActorKind(event: UnsequencedProjectEvent): typeof ActorKind.Type { +function inferActorKind( + event: OrchestrationEventStore.UnsequencedProjectEvent, +): typeof ActorKind.Type { if (event.commandId !== null && event.commandId.startsWith("provider:")) { return "provider"; } @@ -251,30 +249,31 @@ const makeEventStore = Effect.gen(function* () { `, }); - const appendProjectEvent: OrchestrationEventStoreShape["appendProjectEvent"] = (event) => - appendProjectEventRow({ - eventId: event.eventId, - streamId: event.aggregateId, - type: event.type, - causationEventId: event.causationEventId, - correlationId: event.correlationId, - actorKind: inferActorKind(event), - occurredAt: event.occurredAt, - commandId: event.commandId, - payloadJson: - event.type === "project.deleted" || !event.payload.projectIcon - ? event.payload - : { ...event.payload, projectIcon: encodeProjectIcon(event.payload.projectIcon) }, - metadataJson: event.metadata, - }).pipe( - Effect.flatMap((row) => decodeProjectEvent(row)), - Effect.mapError( - toPersistenceSqlOrDecodeError( - "OrchestrationEventStore.appendProjectEvent:insert", - "OrchestrationEventStore.appendProjectEvent:decode", + const appendProjectEvent: OrchestrationEventStore.OrchestrationEventStoreShape["appendProjectEvent"] = + (event) => + appendProjectEventRow({ + eventId: event.eventId, + streamId: event.aggregateId, + type: event.type, + causationEventId: event.causationEventId, + correlationId: event.correlationId, + actorKind: inferActorKind(event), + occurredAt: event.occurredAt, + commandId: event.commandId, + payloadJson: + event.type === "project.deleted" || !event.payload.projectIcon + ? event.payload + : { ...event.payload, projectIcon: encodeProjectIcon(event.payload.projectIcon) }, + metadataJson: event.metadata, + }).pipe( + Effect.flatMap((row) => decodeProjectEvent(row)), + Effect.mapError( + toPersistenceSqlOrDecodeError( + "OrchestrationEventStore.appendProjectEvent:insert", + "OrchestrationEventStore.appendProjectEvent:decode", + ), ), - ), - ); + ); const readApplicationRows = (input: { readonly afterSequence: number; @@ -324,13 +323,14 @@ const makeEventStore = Effect.gen(function* () { LIMIT ${input.limit} `; - const appendAgentEvents: OrchestrationEventStoreShape["appendAgentEvents"] = (input) => - Effect.forEach( - input.events, - (event) => - Effect.gen(function* () { - const encoded = yield* encodeV2EventJson(event); - const rows = yield* sql<{ readonly sequence: number }>` + const appendAgentEvents: OrchestrationEventStore.OrchestrationEventStoreShape["appendAgentEvents"] = + (input) => + Effect.forEach( + input.events, + (event) => + Effect.gen(function* () { + const encoded = yield* encodeV2EventJson(event); + const rows = yield* sql<{ readonly sequence: number }>` INSERT INTO orchestration_events ( event_id, aggregate_kind, @@ -370,23 +370,25 @@ const makeEventStore = Effect.gen(function* () { ) RETURNING sequence `; - return yield* decodeV2StoredEvent({ - sequence: rows[0]?.sequence, - commandId: input.commandId ?? null, - event, - }); - }), - { concurrency: 1 }, - ).pipe( - Effect.mapError( - toPersistenceSqlOrDecodeError( - "OrchestrationEventStore.appendAgentEvents:insert", - "OrchestrationEventStore.appendAgentEvents:decode", + return yield* decodeV2StoredEvent({ + sequence: rows[0]?.sequence, + commandId: input.commandId ?? null, + event, + }); + }), + { concurrency: 1 }, + ).pipe( + Effect.mapError( + toPersistenceSqlOrDecodeError( + "OrchestrationEventStore.appendAgentEvents:insert", + "OrchestrationEventStore.appendAgentEvents:decode", + ), ), - ), - ); + ); - const readAgentEvents: OrchestrationEventStoreShape["readAgentEvents"] = (input) => { + const readAgentEvents: OrchestrationEventStore.OrchestrationEventStoreShape["readAgentEvents"] = ( + input, + ) => { const totalLimit = input?.limit === undefined ? Number.MAX_SAFE_INTEGER : Math.max(0, Math.floor(input.limit)); if (totalLimit === 0) { @@ -434,12 +436,13 @@ const makeEventStore = Effect.gen(function* () { ); }; - const getAgentReplayStats: OrchestrationEventStoreShape["getAgentReplayStats"] = (input) => - sql<{ - readonly eventCount: number; - readonly rawPayloadBytes: number; - readonly hasCreateEvent: number; - }>` + const getAgentReplayStats: OrchestrationEventStore.OrchestrationEventStoreShape["getAgentReplayStats"] = + (input) => + sql<{ + readonly eventCount: number; + readonly rawPayloadBytes: number; + readonly hasCreateEvent: number; + }>` SELECT COUNT(*) AS "eventCount", COALESCE(SUM(octet_length(payload_json)), 0) AS "rawPayloadBytes", @@ -456,15 +459,17 @@ const makeEventStore = Effect.gen(function* () { LIMIT ${Math.max(0, Math.floor(input.maxEvents)) + 1} ) `.pipe( - Effect.mapError(toPersistenceSqlError("OrchestrationEventStore.getAgentReplayStats:query")), - Effect.map((rows) => ({ - eventCount: rows[0]?.eventCount ?? 0, - rawPayloadBytes: rows[0]?.rawPayloadBytes ?? 0, - hasCreateEvent: (rows[0]?.hasCreateEvent ?? 0) !== 0, - })), - ); + Effect.mapError(toPersistenceSqlError("OrchestrationEventStore.getAgentReplayStats:query")), + Effect.map((rows) => ({ + eventCount: rows[0]?.eventCount ?? 0, + rawPayloadBytes: rows[0]?.rawPayloadBytes ?? 0, + hasCreateEvent: (rows[0]?.hasCreateEvent ?? 0) !== 0, + })), + ); - const getReplayStats: OrchestrationEventStoreShape["getReplayStats"] = (input) => + const getReplayStats: OrchestrationEventStore.OrchestrationEventStoreShape["getReplayStats"] = ( + input, + ) => sql<{ readonly eventCount: number; readonly rawPayloadBytes: number }>` SELECT COUNT(*) AS "eventCount", @@ -484,8 +489,9 @@ const makeEventStore = Effect.gen(function* () { })), ); - const latestAgentSequence: OrchestrationEventStoreShape["latestAgentSequence"] = (threadId) => - sql<{ readonly sequence: number | null }>` + const latestAgentSequence: OrchestrationEventStore.OrchestrationEventStoreShape["latestAgentSequence"] = + (threadId) => + sql<{ readonly sequence: number | null }>` SELECT MAX(sequence) AS sequence FROM orchestration_events ${ @@ -497,9 +503,9 @@ const makeEventStore = Effect.gen(function* () { AND aggregate_kind = 'thread' ${threadId === undefined ? sql`` : sql`AND stream_id = ${threadId}`} `.pipe( - Effect.map((rows) => rows[0]?.sequence ?? 0), - Effect.mapError(toPersistenceSqlError("OrchestrationEventStore.latestAgentSequence:query")), - ); + Effect.map((rows) => rows[0]?.sequence ?? 0), + Effect.mapError(toPersistenceSqlError("OrchestrationEventStore.latestAgentSequence:query")), + ); // The OR planner otherwise scans every V2 event instead of seeking the final sequence. const latestApplicationSequence = sql<{ readonly sequence: number | null }>` @@ -562,7 +568,7 @@ const makeEventStore = Effect.gen(function* () { ); }; - const streamProjectedApplicationEvents: OrchestrationEventStoreShape["streamProjectedApplicationEvents"] = + const streamProjectedApplicationEvents: OrchestrationEventStore.OrchestrationEventStoreShape["streamProjectedApplicationEvents"] = (input) => replayAndBufferProjectedLiveEvents({ subscribe: PubSub.subscribe(committedEvents), @@ -582,9 +588,8 @@ const makeEventStore = Effect.gen(function* () { ), ); - const streamApplicationEvents: OrchestrationEventStoreShape["streamApplicationEvents"] = ( - input, - ) => streamProjectedApplicationEvents({ ...input, project: (event) => event }); + const streamApplicationEvents: OrchestrationEventStore.OrchestrationEventStoreShape["streamApplicationEvents"] = + (input) => streamProjectedApplicationEvents({ ...input, project: (event) => event }); return { appendProjectEvent, @@ -598,7 +603,10 @@ const makeEventStore = Effect.gen(function* () { publishCommitted: (events) => PubSub.publishAll(committedEvents, events).pipe(Effect.asVoid), streamApplicationEvents, streamProjectedApplicationEvents, - } satisfies OrchestrationEventStoreShape; + } satisfies OrchestrationEventStore.OrchestrationEventStoreShape; }); -export const OrchestrationEventStoreLive = Layer.effect(OrchestrationEventStore, makeEventStore); +export const OrchestrationEventStoreLive = Layer.effect( + OrchestrationEventStore.OrchestrationEventStore, + makeEventStore, +); diff --git a/apps/server/src/persistence/Layers/Sqlite.ts b/apps/server/src/persistence/Layers/Sqlite.ts index 5d86fae73818..68e700dc23e3 100644 --- a/apps/server/src/persistence/Layers/Sqlite.ts +++ b/apps/server/src/persistence/Layers/Sqlite.ts @@ -7,7 +7,7 @@ import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient"; import { runMigrations } from "../Migrations.ts"; import { initializeV2Database } from "../initializeV2Database.ts"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; // Size the -wal file is cut back to on the first commit after a WAL reset. export const WAL_SIZE_LIMIT_BYTES = 32 * 1024 * 1024; @@ -52,7 +52,7 @@ export const SqlitePersistenceMemory = Layer.provideMerge( export const layerConfig = Layer.unwrap( Effect.gen(function* () { - const { dbPath } = yield* ServerConfig; + const { dbPath } = yield* ServerConfig.ServerConfig; yield* initializeV2Database(dbPath); return makeSqlitePersistenceLive(dbPath); }), diff --git a/apps/server/src/persistence/initializeV2Database.test.ts b/apps/server/src/persistence/initializeV2Database.test.ts index 336af3ed997d..8a96d71324a9 100644 --- a/apps/server/src/persistence/initializeV2Database.test.ts +++ b/apps/server/src/persistence/initializeV2Database.test.ts @@ -12,21 +12,14 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as SqlClient from "effect/unstable/sql/SqlClient"; -import { deriveServerPaths, layer as configLayer, layerTest } from "../config.ts"; -import { ServerConfig } from "../config.ts"; -import { layerConfig } from "./Layers/Sqlite.ts"; +import * as ServerConfig from "../config.ts"; +import * as SqlitePersistence from "./Layers/Sqlite.ts"; import { runMigrations } from "./Migrations.ts"; import { initializeV2Database } from "./initializeV2Database.ts"; -import { layer as eventStoreLayer } from "../orchestration-v2/EventStore.ts"; -import { - ProjectionStoreV2, - layer as projectionStoreLayer, -} from "../orchestration-v2/ProjectionStore.ts"; -import { layer as eventSinkLayer } from "../orchestration-v2/EventSink.ts"; -import { - LegacyV1ThreadImporter, - layer as importerLayer, -} from "../orchestration-v2/legacy/LegacyV1ThreadImporter.ts"; +import * as EventStore from "../orchestration-v2/EventStore.ts"; +import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; +import * as EventSink from "../orchestration-v2/EventSink.ts"; +import * as LegacyV1ThreadImporter from "../orchestration-v2/legacy/LegacyV1ThreadImporter.ts"; it.effect( "snapshots V1, imports transcripts lazily, and preserves both databases across switches", @@ -51,20 +44,22 @@ it.effect( return Effect.gen(function* () { yield* seed; const original = NodeFS.readFileSync(sourcePath); - const config = yield* ServerConfig; - const databaseLayer = layerConfig.pipe( - Layer.provide(configLayer({ ...config, dbPath: destinationPath })), + const config = yield* ServerConfig.ServerConfig; + const databaseLayer = SqlitePersistence.layerConfig.pipe( + Layer.provide(ServerConfig.layer({ ...config, dbPath: destinationPath })), ); - const stores = Layer.mergeAll(eventStoreLayer, projectionStoreLayer).pipe( + const stores = Layer.mergeAll(EventStore.layer, ProjectionStore.layer).pipe( Layer.provideMerge(databaseLayer), ); - const sink = eventSinkLayer.pipe(Layer.provide(stores)); - const importer = importerLayer.pipe(Layer.provideMerge(Layer.mergeAll(stores, sink))); + const sink = EventSink.layer.pipe(Layer.provide(stores)); + const importer = LegacyV1ThreadImporter.layer.pipe( + Layer.provideMerge(Layer.mergeAll(stores, sink)), + ); yield* Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - const legacy = yield* LegacyV1ThreadImporter; + const legacy = yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter; yield* legacy.reconcileShells; - const projections = yield* ProjectionStoreV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const shell = yield* projections.getThreadProjection(threadId); assert.equal(shell.thread.id, threadId); assert.deepEqual( @@ -112,7 +107,9 @@ it.effect( assert.equal((yield* sql`SELECT title FROM projection_threads`)[0]?.title, "V1 thread"); }).pipe(Effect.provide(databaseLayer)); }).pipe( - Effect.provide(layerTest(directory, directory).pipe(Layer.provideMerge(NodeServices.layer))), + Effect.provide( + ServerConfig.layerTest(directory, directory).pipe(Layer.provideMerge(NodeServices.layer)), + ), Effect.ensuring( Effect.sync(() => NodeFS.rmSync(directory, { recursive: true, force: true })), ), @@ -161,7 +158,9 @@ it.effect("uses statev2.sqlite for default and explicit development paths", () = Effect.gen(function* () { for (const devUrl of [undefined, new URL("http://localhost:5173")]) { for (const baseDirIsExplicit of [false, true]) { - const paths = yield* deriveServerPaths("/tmp/t3", devUrl, { baseDirIsExplicit }); + const paths = yield* ServerConfig.deriveServerPaths("/tmp/t3", devUrl, { + baseDirIsExplicit, + }); assert.equal(NodePath.basename(paths.dbPath), "statev2.sqlite"); assert.equal(paths.settingsPath, NodePath.join(paths.stateDir, "settings.json")); } @@ -173,9 +172,9 @@ it.effect("starts fresh without V1 and never imports over existing V2 state", () const directory = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-v2-fresh-")); const destinationPath = NodePath.join(directory, "userdata", "statev2.sqlite"); return Effect.gen(function* () { - const config = yield* ServerConfig; - const database = layerConfig.pipe( - Layer.provide(configLayer({ ...config, dbPath: destinationPath })), + const config = yield* ServerConfig.ServerConfig; + const database = SqlitePersistence.layerConfig.pipe( + Layer.provide(ServerConfig.layer({ ...config, dbPath: destinationPath })), ); yield* Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; @@ -191,7 +190,9 @@ it.effect("starts fresh without V1 and never imports over existing V2 state", () assert.equal((yield* sql`SELECT text FROM v2_work`)[0]?.text, "fresh V2 work"); }).pipe(Effect.provide(database)); }).pipe( - Effect.provide(layerTest(directory, directory).pipe(Layer.provideMerge(NodeServices.layer))), + Effect.provide( + ServerConfig.layerTest(directory, directory).pipe(Layer.provideMerge(NodeServices.layer)), + ), Effect.ensuring(Effect.sync(() => NodeFS.rmSync(directory, { recursive: true, force: true }))), ); }); diff --git a/apps/server/src/project/AgentSessionImporter.test.ts b/apps/server/src/project/AgentSessionImporter.test.ts index 74d6b6d93ccc..b4b2b98f82c2 100644 --- a/apps/server/src/project/AgentSessionImporter.test.ts +++ b/apps/server/src/project/AgentSessionImporter.test.ts @@ -10,13 +10,13 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Stream from "effect/Stream"; -import { EventSinkV2 } from "../orchestration-v2/EventSink.ts"; -import { layer as idAllocatorLayer } from "../orchestration-v2/IdAllocator.ts"; -import { OrchestratorProjectionError, OrchestratorV2 } from "../orchestration-v2/Orchestrator.ts"; -import { ProviderSessionRuntimeRepository } from "../persistence/ProviderSessionRuntime.ts"; -import { AgentSessionImporter, layer } from "./AgentSessionImporter.ts"; +import * as EventSink from "../orchestration-v2/EventSink.ts"; +import * as IdAllocator from "../orchestration-v2/IdAllocator.ts"; +import * as Orchestrator from "../orchestration-v2/Orchestrator.ts"; +import * as ProviderSessionRuntime from "../persistence/ProviderSessionRuntime.ts"; +import * as AgentSessionImporter from "./AgentSessionImporter.ts"; import * as AgentSessionScanner from "./AgentSessionScanner.ts"; -import { ProjectService } from "./ProjectService.ts"; +import * as ProjectService from "./ProjectService.ts"; const projectId = ProjectId.make("agent-session-import-project"); const providerInstanceId = ProviderInstanceId.make("codex"); @@ -59,25 +59,25 @@ it.effect("imports messages once and preserves the provider native resume bindin }, }), }); - const testLayer = layer.pipe( + const testLayer = AgentSessionImporter.layer.pipe( Layer.provide( Layer.mergeAll( Layer.succeed(AgentSessionScanner.AgentSessionScanner, scanner), - Layer.mock(ProjectService)({ + Layer.mock(ProjectService.ProjectService)({ getById: () => Effect.succeed( Option.some({ id: projectId, workspaceRoot: "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/workspace/project" } as never), ), }), - Layer.mock(OrchestratorV2)({ + Layer.mock(Orchestrator.OrchestratorV2)({ getThreadRecords: () => imported ? Effect.succeed({ thread: { id: threadId, projectId, historyOrigin: "v1_import" }, } as never) - : Effect.fail(new OrchestratorProjectionError({ threadId })), + : Effect.fail(new Orchestrator.OrchestratorProjectionError({ threadId })), }), - Layer.mock(EventSinkV2)({ + Layer.mock(EventSink.EventSinkV2)({ write: (input) => Effect.sync(() => { writes.push(input.events); @@ -85,18 +85,18 @@ it.effect("imports messages once and preserves the provider native resume bindin return []; }), }), - Layer.mock(ProviderSessionRuntimeRepository)({ + Layer.mock(ProviderSessionRuntime.ProviderSessionRuntimeRepository)({ list: () => Effect.succeed([]), upsert: (input) => Effect.sync(() => void upserts.push(input)), recordImportedTranscript: (input) => Effect.sync(() => void recorded.push(input)), }), - idAllocatorLayer, + IdAllocator.layer, ), ), ); return Effect.gen(function* () { - const importer = yield* AgentSessionImporter; + const importer = yield* AgentSessionImporter.AgentSessionImporter; expect(yield* importer.importRecentAgentThreads({ projectId })).toEqual({ importedCount: 1, skippedCount: 0, diff --git a/apps/server/src/project/AgentSessionImporter.ts b/apps/server/src/project/AgentSessionImporter.ts index 863e654d507a..43ce8096b8ea 100644 --- a/apps/server/src/project/AgentSessionImporter.ts +++ b/apps/server/src/project/AgentSessionImporter.ts @@ -31,12 +31,12 @@ import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; -import { EventSinkV2 } from "../orchestration-v2/EventSink.ts"; -import { IdAllocatorV2 } from "../orchestration-v2/IdAllocator.ts"; -import { OrchestratorV2 } from "../orchestration-v2/Orchestrator.ts"; +import * as EventSink from "../orchestration-v2/EventSink.ts"; +import * as IdAllocator from "../orchestration-v2/IdAllocator.ts"; +import * as Orchestrator from "../orchestration-v2/Orchestrator.ts"; import * as ProviderSessionRuntime from "../persistence/ProviderSessionRuntime.ts"; import * as AgentSessionScanner from "./AgentSessionScanner.ts"; -import { ProjectService } from "./ProjectService.ts"; +import * as ProjectService from "./ProjectService.ts"; const IMPORT_EVENT_PREFIX = "agent-session-import:v2"; const CLAUDE_SESSION_ID_PATTERN = @@ -167,10 +167,10 @@ function messageEvents(input: { const make = Effect.gen(function* () { const scanner = yield* AgentSessionScanner.AgentSessionScanner; - const orchestrator = yield* OrchestratorV2; - const projects = yield* ProjectService; - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const projects = yield* ProjectService.ProjectService; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const runtimes = yield* ProviderSessionRuntime.ProviderSessionRuntimeRepository; const importRecentAgentThreads = Effect.fn("importRecentAgentThreadsV2")(function* ( input: AgentSessionImportInput, diff --git a/apps/server/src/project/ProjectService.deletion.test.ts b/apps/server/src/project/ProjectService.deletion.test.ts index 3bae6045e109..955baa79a79d 100644 --- a/apps/server/src/project/ProjectService.deletion.test.ts +++ b/apps/server/src/project/ProjectService.deletion.test.ts @@ -16,30 +16,16 @@ import * as Schema from "effect/Schema"; import { TestClock } from "effect/testing"; import * as SqlClient from "effect/unstable/sql/SqlClient"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import { OrchestrationEffectRequestV2 } from "../orchestration-v2/EffectOutbox.ts"; -import { - EventSinkV2, - type EventSinkV2Shape, - EventSinkWriteError, - layer as eventSinkLayer, -} from "../orchestration-v2/EventSink.ts"; -import { layer as eventStoreLayer } from "../orchestration-v2/EventStore.ts"; -import { IdAllocatorV2, layer as idAllocatorLayer } from "../orchestration-v2/IdAllocator.ts"; -import { - LegacyV1ThreadImporter, - layer as legacyImporterLayer, -} from "../orchestration-v2/legacy/LegacyV1ThreadImporter.ts"; -import { - ProjectionMaintenanceV2, - layer as projectionMaintenanceLayer, -} from "../orchestration-v2/ProjectionMaintenance.ts"; -import { - ProjectionStoreV2, - layer as projectionStoreLayer, -} from "../orchestration-v2/ProjectionStore.ts"; -import { layer as projectStoreLayer } from "../orchestration-v2/ProjectStore.ts"; -import { layer as threadCommandExecutorLayer } from "../orchestration-v2/ThreadCommandExecutor.ts"; +import * as EventSink from "../orchestration-v2/EventSink.ts"; +import * as EventStore from "../orchestration-v2/EventStore.ts"; +import * as IdAllocator from "../orchestration-v2/IdAllocator.ts"; +import * as LegacyV1ThreadImporter from "../orchestration-v2/legacy/LegacyV1ThreadImporter.ts"; +import * as ProjectionMaintenance from "../orchestration-v2/ProjectionMaintenance.ts"; +import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; +import * as ProjectStore from "../orchestration-v2/ProjectStore.ts"; +import * as ThreadCommandExecutor from "../orchestration-v2/ThreadCommandExecutor.ts"; import { planThreadDeletion } from "../orchestration-v2/ThreadDeletion.ts"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; @@ -48,15 +34,15 @@ import * as ProjectFaviconResolver from "./ProjectFaviconResolver.ts"; import * as ProjectService from "./ProjectService.ts"; import * as RepositoryIdentityResolver from "./RepositoryIdentityResolver.ts"; -const eventPersistenceLayer = eventSinkLayer.pipe( - Layer.provideMerge(Layer.merge(eventStoreLayer, projectionStoreLayer)), +const eventPersistenceLayer = EventSink.layer.pipe( + Layer.provideMerge(Layer.merge(EventStore.layer, ProjectionStore.layer)), ); const servicesLayer = Layer.mergeAll( - legacyImporterLayer.pipe(Layer.provideMerge(eventPersistenceLayer)), - projectionMaintenanceLayer.pipe(Layer.provide(eventPersistenceLayer)), - projectStoreLayer, - idAllocatorLayer, - threadCommandExecutorLayer, + LegacyV1ThreadImporter.layer.pipe(Layer.provideMerge(eventPersistenceLayer)), + ProjectionMaintenance.layer.pipe(Layer.provide(eventPersistenceLayer)), + ProjectStore.layer, + IdAllocator.layer, + ThreadCommandExecutor.layer, Layer.succeed(WorkspacePaths.WorkspacePaths, { normalizeWorkspaceRoot: (workspaceRoot) => Effect.succeed(workspaceRoot), resolveRelativePathWithinRoot: ({ workspaceRoot, relativePath }) => @@ -145,20 +131,20 @@ it.effect("retries a partial project deletion without repeating child events or yield* TestClock.setTime(Date.parse("2026-09-04T12:00:00.000Z")); yield* Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const projections = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; yield* eventSink.write({ events: threadIds.map((threadId) => nativeThreadCreated(projectId, threadId)), }); const attempts: ThreadId[] = []; - const failingEventSink = EventSinkV2.of({ + const failingEventSink = EventSink.EventSinkV2.of({ ...eventSink, commitCommand: Effect.fn("ProjectDeletionTest.failSecondChild")(function* ( - input: Parameters[0], + input: Parameters[0], ) { attempts.push(input.threadId); if (attempts.length === 2) { - return yield* new EventSinkWriteError({ + return yield* new EventSink.EventSinkWriteError({ commandId: input.commandId, eventCount: input.events.length, cause: new Error("Injected failure for the second child"), @@ -168,7 +154,7 @@ it.effect("retries a partial project deletion without repeating child events or }), }); const service = yield* ProjectService.make.pipe( - Effect.provideService(EventSinkV2, failingEventSink), + Effect.provideService(EventSink.EventSinkV2, failingEventSink), ); const input = { commandId, projectId, force: true }; const failure = yield* service.delete(input).pipe(Effect.flip); @@ -304,9 +290,9 @@ it.effect( // Build the engine after seeding the legacy database, as on a real restart. yield* Effect.gen(function* () { - const importer = yield* LegacyV1ThreadImporter; - const maintenance = yield* ProjectionMaintenanceV2; - const projections = yield* ProjectionStoreV2; + const importer = yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const service = yield* ProjectService.make; assert.deepEqual(yield* importer.reconcileShells, { importedThreadCount: 1, @@ -384,8 +370,8 @@ it.effect("rejects a child deletion command ID already accepted for an unrelated yield* seedProject(otherProjectId); yield* Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const projections = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const service = yield* ProjectService.make; yield* eventSink.write({ events: [nativeThreadCreated(projectId, threadId)] }); const accepted = yield* eventSink.commitCommand({ @@ -443,10 +429,10 @@ it.effect("deletes a project without force once its imported threads were delete yield* TestClock.setTime(Date.parse("2026-09-04T12:00:00.000Z")); yield* Effect.gen(function* () { - const importer = yield* LegacyV1ThreadImporter; - const projections = yield* ProjectionStoreV2; - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; + const importer = yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter; + const projections = yield* ProjectionStore.ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const service = yield* ProjectService.make; yield* importer.reconcileShells; const early = yield* service diff --git a/apps/server/src/project/ProjectService.test.ts b/apps/server/src/project/ProjectService.test.ts index fefa9dc33074..98e576a6fe9b 100644 --- a/apps/server/src/project/ProjectService.test.ts +++ b/apps/server/src/project/ProjectService.test.ts @@ -10,8 +10,8 @@ import * as Ref from "effect/Ref"; import { TestClock } from "effect/testing"; import * as SqlClient from "effect/unstable/sql/SqlClient"; -import { ServerConfig } from "../config.ts"; -import { EventSinkV2 } from "../orchestration-v2/EventSink.ts"; +import * as ServerConfig from "../config.ts"; +import * as EventSink from "../orchestration-v2/EventSink.ts"; import * as IdAllocator from "../orchestration-v2/IdAllocator.ts"; import * as LegacyV1ThreadImporter from "../orchestration-v2/legacy/LegacyV1ThreadImporter.ts"; import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; @@ -20,7 +20,7 @@ import { OrchestrationV2EventSinkLayerLive, ProjectServiceLayerLive, } from "../orchestration-v2/runtimeLayer.ts"; -import { layer as threadCommandExecutorLayer } from "../orchestration-v2/ThreadCommandExecutor.ts"; +import * as ThreadCommandExecutor from "../orchestration-v2/ThreadCommandExecutor.ts"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; import * as ProjectEnrichmentService from "./ProjectEnrichmentService.ts"; @@ -75,7 +75,7 @@ const ProjectServiceDependenciesLayer = Layer.mergeAll( ProjectStore.layer, ProjectionStore.layer, IdAllocator.layer, - threadCommandExecutorLayer, + ThreadCommandExecutor.layer, ).pipe( Layer.provideMerge( LegacyV1ThreadImporter.layer.pipe(Layer.provide(OrchestrationV2EventSinkLayerLive)), @@ -706,11 +706,11 @@ it.effect("invalidates workspace-derived metadata when a project moves", () => it.effect("serializes two projects claiming the same workspace root", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; + const eventSink = yield* EventSink.EventSinkV2; const firstReachedCommit = yield* Deferred.make(); const releaseFirst = yield* Deferred.make(); // Hold the first claim between its plan and its commit. - const gatedSink = EventSinkV2.of({ + const gatedSink = EventSink.EventSinkV2.of({ ...eventSink, commitProjectCommand: (input) => input.projectId === "project:race:first" @@ -720,7 +720,9 @@ it.effect("serializes two projects claiming the same workspace root", () => ) : eventSink.commitProjectCommand(input), }); - const service = yield* ProjectService.make.pipe(Effect.provideService(EventSinkV2, gatedSink)); + const service = yield* ProjectService.make.pipe( + Effect.provideService(EventSink.EventSinkV2, gatedSink), + ); const claim = (name: string) => service.create({ commandId: CommandId.make(`command:race:${name}`), @@ -744,11 +746,11 @@ it.effect("serializes two projects claiming the same workspace root", () => it.effect("rejects an update that waited on the lock while its project was deleted", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; + const eventSink = yield* EventSink.EventSinkV2; const deleteReachedCommit = yield* Deferred.make(); const releaseDelete = yield* Deferred.make(); // Hold the delete between its plan and its commit, inside the project lock. - const gatedSink = EventSinkV2.of({ + const gatedSink = EventSink.EventSinkV2.of({ ...eventSink, commitProjectCommand: (input) => input.commandType === "project.delete" @@ -758,7 +760,9 @@ it.effect("rejects an update that waited on the lock while its project was delet ) : eventSink.commitProjectCommand(input), }); - const service = yield* ProjectService.make.pipe(Effect.provideService(EventSinkV2, gatedSink)); + const service = yield* ProjectService.make.pipe( + Effect.provideService(EventSink.EventSinkV2, gatedSink), + ); const sql = yield* SqlClient.SqlClient; const projectId = ProjectId.make("project:update-race"); yield* service.create({ diff --git a/apps/server/src/project/ProjectService.ts b/apps/server/src/project/ProjectService.ts index a6c0d63aa8ec..5d0e6b68e626 100644 --- a/apps/server/src/project/ProjectService.ts +++ b/apps/server/src/project/ProjectService.ts @@ -16,11 +16,11 @@ import * as Option from "effect/Option"; import * as Result from "effect/Result"; import * as Schema from "effect/Schema"; -import { EventSinkV2 } from "../orchestration-v2/EventSink.ts"; +import * as EventSink from "../orchestration-v2/EventSink.ts"; import * as IdAllocator from "../orchestration-v2/IdAllocator.ts"; import { makeKeyedSerialExecutor } from "../orchestration-v2/KeyedSerialExecutor.ts"; import * as LegacyV1ThreadImporter from "../orchestration-v2/legacy/LegacyV1ThreadImporter.ts"; -import { ProjectionStoreV2 } from "../orchestration-v2/ProjectionStore.ts"; +import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; import { decodeProjectCommandRejection, encodeProjectCommandRejection, @@ -28,12 +28,9 @@ import { type ProjectCommand, } from "../orchestration-v2/ProjectCommands.ts"; import * as ProjectStore from "../orchestration-v2/ProjectStore.ts"; -import { - ThreadCommandExecutor, - layer as threadCommandExecutorLayer, -} from "../orchestration-v2/ThreadCommandExecutor.ts"; +import * as ThreadCommandExecutor from "../orchestration-v2/ThreadCommandExecutor.ts"; import { planThreadDeletion } from "../orchestration-v2/ThreadDeletion.ts"; -import { ProjectEnrichmentService, type ProjectEnrichment } from "./ProjectEnrichmentService.ts"; +import * as ProjectEnrichmentService from "./ProjectEnrichmentService.ts"; import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; export interface ProjectCreateInput extends ProjectCreatePayload { @@ -149,13 +146,13 @@ export class ProjectService extends Context.Service< export const make = Effect.gen(function* () { const projects = yield* ProjectStore.ProjectStoreV2; - const projectEnrichment = yield* ProjectEnrichmentService; + const projectEnrichment = yield* ProjectEnrichmentService.ProjectEnrichmentService; const workspacePaths = yield* WorkspacePaths.WorkspacePaths; - const threadProjections = yield* ProjectionStoreV2; - const eventSink = yield* EventSinkV2; + const threadProjections = yield* ProjectionStore.ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; const idAllocator = yield* IdAllocator.IdAllocatorV2; const legacyImporter = yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter; - const threadCommands = yield* ThreadCommandExecutor; + const threadCommands = yield* ThreadCommandExecutor.ThreadCommandExecutor; // Commands for one project run in order. Commands that claim a workspace root // also hold that root, so two projects cannot both claim it. const projectLocks = yield* makeKeyedSerialExecutor(); @@ -163,7 +160,7 @@ export const make = Effect.gen(function* () { const toProject = ( row: ProjectStore.ProjectRow, - enrichment: ProjectEnrichment | null, + enrichment: ProjectEnrichmentService.ProjectEnrichment | null, ): Project => ({ id: row.projectId, title: row.title, @@ -571,5 +568,5 @@ export const make = Effect.gen(function* () { }); export const layer = Layer.effect(ProjectService, make).pipe( - Layer.provide(threadCommandExecutorLayer), + Layer.provide(ThreadCommandExecutor.layer), ); diff --git a/apps/server/src/project/http.ts b/apps/server/src/project/http.ts index 12e3bfc92d4c..1d4e591233b3 100644 --- a/apps/server/src/project/http.ts +++ b/apps/server/src/project/http.ts @@ -13,11 +13,11 @@ import { requireEnvironmentScope, } from "../auth/http.ts"; import * as ServerRuntimeStartup from "../serverRuntimeStartup.ts"; -import { ProjectService, type ProjectServiceError } from "./ProjectService.ts"; +import * as ProjectService from "./ProjectService.ts"; import { projectMutationOperation } from "./ProjectMutation.ts"; export const failProjectMutation = Effect.fn("environment.projects.failMutation")(function* ( - cause: ProjectServiceError | ServerRuntimeStartup.ServerRuntimeStartupError, + cause: ProjectService.ProjectServiceError | ServerRuntimeStartup.ServerRuntimeStartupError, ) { if ( cause._tag === "ProjectNotFoundError" || @@ -33,7 +33,7 @@ export const projectHttpApiLayer = HttpApiBuilder.group( EnvironmentHttpApi, "projects", Effect.fnUntraced(function* (handlers) { - const projects = yield* ProjectService; + const projects = yield* ProjectService.ProjectService; const startup = yield* ServerRuntimeStartup.ServerRuntimeStartup; return handlers diff --git a/apps/server/src/provider/AntigravityInstallation.test.ts b/apps/server/src/provider/AntigravityInstallation.test.ts index 8c3f3468372d..0d709820408d 100644 --- a/apps/server/src/provider/AntigravityInstallation.test.ts +++ b/apps/server/src/provider/AntigravityInstallation.test.ts @@ -23,12 +23,7 @@ import { HttpClient, HttpClientResponse } from "effect/unstable/http"; import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner"; import * as NodeCrypto from "node:crypto"; -import { - makeAntigravityInstallation, - type AntigravityExecutable, - type AntigravityInstallation, - type AntigravityInstallationOptions, -} from "./AntigravityInstallation.ts"; +import * as AntigravityInstallation from "./AntigravityInstallation.ts"; import { ANTIGRAVITY_AUTH_BROWSER_MARKER } from "./antigravityAuthSupport.ts"; import type { AntigravityReleaseAsset } from "./antigravityRelease.ts"; @@ -135,7 +130,7 @@ interface HarnessOptions { readonly path?: string; readonly previous?: boolean; readonly fileSystem?: FileSystem.FileSystem; - readonly validate?: AntigravityInstallationOptions["validate"]; + readonly validate?: AntigravityInstallation.AntigravityInstallationOptions["validate"]; readonly useDefaultValidation?: boolean; } @@ -159,7 +154,10 @@ const makeHarness = Effect.fn("test.makeAntigravityInstallation")(function* ( } const stagingReleased = yield* Deferred.make(); const requests: string[] = []; - const validations: Array<{ executable: AntigravityExecutable; version: string }> = []; + const validations: Array<{ + executable: AntigravityInstallation.AntigravityExecutable; + version: string; + }> = []; const installationFs = options.fileSystem ?? fs; const trackedFs = FileSystem.FileSystem.of({ ...installationFs, @@ -172,13 +170,13 @@ const makeHarness = Effect.fn("test.makeAntigravityInstallation")(function* ( ) : installationFs.makeTempDirectoryScoped(settings), }); - const installation = yield* makeAntigravityInstallation({ + const installation = yield* AntigravityInstallation.makeAntigravityInstallation({ baseDir, releaseAsset: asset, ...(options.useDefaultValidation ? {} : { - validate: (executable: AntigravityExecutable, version: string) => + validate: (executable: AntigravityInstallation.AntigravityExecutable, version: string) => Effect.sync(() => validations.push({ executable, version })).pipe( Effect.andThen(options.validate?.(executable, version) ?? Effect.void), ), @@ -222,7 +220,7 @@ const makeHarness = Effect.fn("test.makeAntigravityInstallation")(function* ( return { installation, fs, path, baseDir, requests, validations, stagingReleased }; }); -const terminalState = (installation: AntigravityInstallation["Service"]) => +const terminalState = (installation: AntigravityInstallation.AntigravityInstallation["Service"]) => installation.changes.pipe( Stream.filter((state) => ["succeeded", "failed", "cancelled"].includes(state.phase)), Stream.runHead, @@ -230,7 +228,7 @@ const terminalState = (installation: AntigravityInstallation["Service"]) => ); const expectPreviousRelease = Effect.fn("test.expectPreviousAntigravityRelease")(function* ( - installation: AntigravityInstallation["Service"], + installation: AntigravityInstallation.AntigravityInstallation["Service"], ) { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; diff --git a/apps/server/src/provider/AntigravityInstallation.ts b/apps/server/src/provider/AntigravityInstallation.ts index 141e8d1139c1..a0a6d81487ed 100644 --- a/apps/server/src/provider/AntigravityInstallation.ts +++ b/apps/server/src/provider/AntigravityInstallation.ts @@ -30,7 +30,7 @@ import * as NodeFSP from "node:fs/promises"; import type * as NodeStream from "node:stream"; import * as Yauzl from "yauzl"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import { makeAntigravityAcpRuntime } from "./acp/AntigravityAcpSupport.ts"; import { buildAntigravityAcpSpawnInput, @@ -111,7 +111,7 @@ export class AntigravityInstallation extends Context.Service< static readonly layer = Layer.effect( AntigravityInstallation, Effect.gen(function* () { - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; return yield* makeAntigravityInstallation({ baseDir: config.baseDir }); }), ); diff --git a/apps/server/src/provider/CodexChatGptAuth.test.ts b/apps/server/src/provider/CodexChatGptAuth.test.ts index e7166968b498..725314b27e1a 100644 --- a/apps/server/src/provider/CodexChatGptAuth.test.ts +++ b/apps/server/src/provider/CodexChatGptAuth.test.ts @@ -14,11 +14,11 @@ import * as TestClock from "effect/testing/TestClock"; import { subscribeChatGptHandoff } from "./CodexChatGptHandoff.ts"; import { FetchHttpClient } from "effect/unstable/http"; import { exportJWK, generateKeyPair, SignJWT } from "jose"; -import { ServerSecretStore } from "../auth/ServerSecretStore.ts"; -import { ServerEnvironmentIdentity } from "../environment/ServerEnvironment.ts"; +import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; +import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; import * as ProviderCredentialStore from "./ProviderCredentialStore.ts"; -import { layerTest as settingsLayerTest } from "../serverSettings.ts"; -import { AnalyticsService } from "../telemetry/AnalyticsService.ts"; +import * as ServerSettings from "../serverSettings.ts"; +import * as AnalyticsService from "../telemetry/AnalyticsService.ts"; import { makeCodexChatGptAuth } from "./CodexChatGptAuth.ts"; const assertSameCallback = (actual: string | null, expected: string | null) => { @@ -37,13 +37,13 @@ const makeHarnessFor = Effect.fnUntraced(function* ( ) { const environmentId = environmentIds.get(bytes) ?? EnvironmentId.make(NodeCrypto.randomUUID()); environmentIds.set(bytes, environmentId); - const environment = ServerEnvironmentIdentity.of({ + const environment = ServerEnvironment.ServerEnvironmentIdentity.of({ getEnvironmentId: Effect.succeed(environmentId), }); const keys = yield* Effect.promise(() => generateKeyPair("RS256")); const jwk = yield* Effect.promise(() => exportJWK(keys.publicKey)); const untrustedKeys = yield* Effect.promise(() => generateKeyPair("RS256")); - const secrets = ServerSecretStore.of({ + const secrets = ServerSecretStore.ServerSecretStore.of({ get: (name) => Effect.sync(() => Option.fromUndefinedOr(bytes.get(name))), set: (name, value) => Effect.sync(() => { @@ -212,7 +212,7 @@ const makeHarnessFor = Effect.fnUntraced(function* ( event: string; properties: Readonly> | undefined; }[] = []; - const analytics = AnalyticsService.of({ + const analytics = AnalyticsService.AnalyticsService.of({ record: (event, properties) => failAnalytics ? Effect.die("analytics unavailable") @@ -226,9 +226,9 @@ const makeHarnessFor = Effect.fnUntraced(function* ( discoveryUrl: `${origin}/discovery`, resource: `${origin}/v1`, }).pipe( - Effect.provideService(AnalyticsService, analytics), - Effect.provideService(ServerSecretStore, secrets), - Effect.provideService(ServerEnvironmentIdentity, environment), + Effect.provideService(AnalyticsService.AnalyticsService, analytics), + Effect.provideService(ServerSecretStore.ServerSecretStore, secrets), + Effect.provideService(ServerEnvironment.ServerEnvironmentIdentity, environment), ); const phase = (phase: string) => auth.controller.subscribe("owner").pipe( @@ -272,7 +272,7 @@ const makeHarnessFor = Effect.fnUntraced(function* ( const stored = yield* auth.read; const record = Option.getOrThrow(stored); const store = yield* ProviderCredentialStore.make("codex-chatgpt", instanceId).pipe( - Effect.provideService(ServerSecretStore, secrets), + Effect.provideService(ServerSecretStore.ServerSecretStore, secrets), ); yield* store.set(new TextEncoder().encode(JSON.stringify({ ...record, expiresAt: 0 }))); }); @@ -293,12 +293,12 @@ const makeHarnessFor = Effect.fnUntraced(function* ( }, "owner", { discoveryUrl: `${origin}/discovery`, resource: `${origin}/v1` }, - ).pipe(Stream.provideService(AnalyticsService, analytics)), + ).pipe(Stream.provideService(AnalyticsService.AnalyticsService, analytics)), finishCallback: (state: { authorizationUrl: string | null }) => Effect.promise(() => fetch(prepareCallback(state))), destination: Effect.gen(function* () { const destinationBytes = new Map(); - const destinationStore = ServerSecretStore.of({ + const destinationStore = ServerSecretStore.ServerSecretStore.of({ ...secrets, get: (name) => Effect.sync(() => Option.fromUndefinedOr(destinationBytes.get(name))), set: (name, value) => @@ -315,9 +315,9 @@ const makeHarnessFor = Effect.fnUntraced(function* ( discoveryUrl: `${origin}/discovery`, resource: `${origin}/v1`, }).pipe( - Effect.provideService(AnalyticsService, analytics), - Effect.provideService(ServerSecretStore, destinationStore), - Effect.provideService(ServerEnvironmentIdentity, environment), + Effect.provideService(AnalyticsService.AnalyticsService, analytics), + Effect.provideService(ServerSecretStore.ServerSecretStore, destinationStore), + Effect.provideService(ServerEnvironment.ServerEnvironmentIdentity, environment), ); return { auth: destination, bytes: destinationBytes }; }), @@ -326,8 +326,8 @@ const makeHarnessFor = Effect.fnUntraced(function* ( discoveryUrl: `${origin}/discovery`, resource: `${origin}/v1`, }).pipe( - Effect.provideService(ServerSecretStore, secrets), - Effect.provideService(ServerEnvironmentIdentity, environment), + Effect.provideService(ServerSecretStore.ServerSecretStore, secrets), + Effect.provideService(ServerEnvironment.ServerEnvironmentIdentity, environment), ), startRemote, signIn: signInWithMethod(), @@ -1737,8 +1737,8 @@ it.effect("primary completes OAuth and destination imports and owns the refresh assert.isFalse("refreshToken" in reconnect!); const store = yield* ProviderCredentialStore.make("codex-chatgpt", instanceId).pipe( Effect.provideService( - ServerSecretStore, - ServerSecretStore.of({ + ServerSecretStore.ServerSecretStore, + ServerSecretStore.ServerSecretStore.of({ get: (name) => Effect.sync(() => Option.fromUndefinedOr(destination.bytes.get(name))), set: (name, value) => Effect.sync(() => { @@ -1987,7 +1987,7 @@ it.effect("includes accounts from other Codex instances in the environment", () assert.strictEqual(completed?.properties?.savedConnectionCount, 2); }).pipe( Effect.provide( - settingsLayerTest({ + ServerSettings.layerTest({ providerInstances: { [instanceId]: { driver: "codex", enabled: true }, [ProviderInstanceId.make("managed-work")]: { driver: "codex", enabled: true }, @@ -2003,7 +2003,7 @@ it.effect("an unreadable unrelated profile omits counts without failing sign-in" Effect.gen(function* () { const h = yield* makeHarness; const unrelated = yield* ProviderCredentialStore.make("codex-chatgpt", "codex").pipe( - Effect.provideService(ServerSecretStore, h.secrets), + Effect.provideService(ServerSecretStore.ServerSecretStore, h.secrets), ); // The harness owns its store; seed the corresponding binding in that store. h.bytes.set(unrelated.binding.key, new TextEncoder().encode("invalid")); @@ -2012,7 +2012,7 @@ it.effect("an unreadable unrelated profile omits counts without failing sign-in" const completed = h.analyticsEvents.find(({ event }) => event === "chatgpt.auth.completed"); assert.strictEqual(completed?.properties?.outcome, "succeeded"); assert.notProperty(completed?.properties ?? {}, "connectedAccountCount"); - }).pipe(Effect.provide(settingsLayerTest())), + }).pipe(Effect.provide(ServerSettings.layerTest())), ), ); @@ -2023,7 +2023,7 @@ it.effect("reports connections without an email separately from identifiable acc yield* h.signIn; yield* h.phase("succeeded"); const store = yield* ProviderCredentialStore.make("codex-chatgpt", instanceId).pipe( - Effect.provideService(ServerSecretStore, h.secrets), + Effect.provideService(ServerSecretStore.ServerSecretStore, h.secrets), ); yield* store.set( new TextEncoder().encode( diff --git a/apps/server/src/provider/CodexChatGptAuth.ts b/apps/server/src/provider/CodexChatGptAuth.ts index 365025dbb09c..210cc9e96842 100644 --- a/apps/server/src/provider/CodexChatGptAuth.ts +++ b/apps/server/src/provider/CodexChatGptAuth.ts @@ -11,7 +11,7 @@ import { import { codexCallbackUrl } from "@t3tools/shared/codexAuthHandoff"; import * as Clock from "effect/Clock"; import * as Cause from "effect/Cause"; -import { AnalyticsService } from "../telemetry/AnalyticsService.ts"; +import * as AnalyticsService from "../telemetry/AnalyticsService.ts"; import * as Exit from "effect/Exit"; import { codexAuthCallbackPage, codexAuthReturnUrl } from "./CodexAuthCallbackPage.ts"; import * as Effect from "effect/Effect"; @@ -23,9 +23,9 @@ import type { ProviderAuthFlowContext } from "./ProviderAuthFlow.ts"; import { HttpClient, HttpClientRequest } from "effect/unstable/http"; import * as ProviderCredentialStore from "./ProviderCredentialStore.ts"; import { withChatGptSessionLock } from "./CodexChatGptSessionLock.ts"; -import { ServerSecretStore } from "../auth/ServerSecretStore.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; -import { ServerEnvironmentIdentity } from "../environment/ServerEnvironment.ts"; +import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; +import * as ServerSettings from "../serverSettings.ts"; +import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; const isSetupError = Schema.is(ProviderSetupError); const RESOURCE = "https://api.openai.com/v1"; @@ -58,7 +58,7 @@ const Sessions = Schema.Struct({ sessions: Schema.Array(Record), }); const sessionLocks = new WeakMap< - typeof ServerSecretStore.Service, + typeof ServerSecretStore.ServerSecretStore.Service, Map >(); const Registration = Schema.Struct({ @@ -106,8 +106,8 @@ export const makeCodexChatGptAuth = Effect.fn("makeCodexChatGptAuth")(function* readonly reconnectProfile?: ChatGptReconnectProfile | null; readonly telemetryFlow?: "direct" | "primary_handoff"; }) { - const analytics = yield* Effect.serviceOption(AnalyticsService); - const settings = yield* Effect.serviceOption(ServerSettingsService); + const analytics = yield* Effect.serviceOption(AnalyticsService.AnalyticsService); + const settings = yield* Effect.serviceOption(ServerSettings.ServerSettingsService); const track = ( event: "auth" | "transfer", properties: Readonly>, @@ -157,14 +157,14 @@ export const makeCodexChatGptAuth = Effect.fn("makeCodexChatGptAuth")(function* "codex-chatgpt-registration", options.instanceId, ); - const secrets = yield* ServerSecretStore; + const secrets = yield* ServerSecretStore.ServerSecretStore; const environmentLocks = sessionLocks.get(secrets) ?? new Map(); sessionLocks.set(secrets, environmentLocks); const lock = environmentLocks.get(store.binding.key) ?? (yield* Semaphore.make(1)); environmentLocks.set(store.binding.key, lock); const withSessionLock = (task: Effect.Effect) => withChatGptSessionLock(secrets.directory, store.binding.key, options.instanceId, task); - const environment = yield* ServerEnvironmentIdentity; + const environment = yield* ServerEnvironment.ServerEnvironmentIdentity; const hostId = `urn:uuid:${yield* environment.getEnvironmentId}`; const resource = options.resource ?? RESOURCE; const failure = (operation: string, detail: string) => @@ -306,7 +306,7 @@ export const makeCodexChatGptAuth = Effect.fn("makeCodexChatGptAuth")(function* savedConnectionCount: savedConnections.size, unidentifiedConnectedConnectionCount, }; - }).pipe(Effect.provideService(ServerSecretStore, secrets)); + }).pipe(Effect.provideService(ServerSecretStore.ServerSecretStore, secrets)); const saveRegistration = Effect.fnUntraced(function* (profile: typeof Registration.Type) { const saved = yield* readRegistrations; profile = { diff --git a/apps/server/src/provider/CodexChatGptHandoff.ts b/apps/server/src/provider/CodexChatGptHandoff.ts index 2a1dce4a6a96..f50c211a2d04 100644 --- a/apps/server/src/provider/CodexChatGptHandoff.ts +++ b/apps/server/src/provider/CodexChatGptHandoff.ts @@ -4,8 +4,8 @@ import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; import * as Stream from "effect/Stream"; import { FetchHttpClient } from "effect/unstable/http"; -import { ServerSecretStore } from "../auth/ServerSecretStore.ts"; -import { ServerEnvironmentIdentity } from "../environment/ServerEnvironment.ts"; +import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; +import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; import { makeCodexChatGptAuth } from "./CodexChatGptAuth.ts"; // The primary owns OAuth for this stream; the destination owns the refresh session. @@ -18,7 +18,7 @@ export function subscribeChatGptHandoff( Effect.gen(function* () { const bytes = new Map(); yield* Effect.addFinalizer(() => Effect.sync(() => bytes.clear())); - const store = ServerSecretStore.of({ + const store = ServerSecretStore.ServerSecretStore.of({ get: (key) => Effect.sync(() => Option.fromUndefinedOr(bytes.get(key))), set: (key, value) => Effect.sync(() => { @@ -38,10 +38,10 @@ export function subscribeChatGptHandoff( telemetryFlow: "primary_handoff", defaultReturnUrl: input.returnUrl, }).pipe( - Effect.provideService(ServerSecretStore, store), + Effect.provideService(ServerSecretStore.ServerSecretStore, store), Effect.provideService( - ServerEnvironmentIdentity, - ServerEnvironmentIdentity.of({ + ServerEnvironment.ServerEnvironmentIdentity, + ServerEnvironment.ServerEnvironmentIdentity.of({ getEnvironmentId: Effect.succeed(input.environmentId), }), ), diff --git a/apps/server/src/provider/CodexInstallation.test.ts b/apps/server/src/provider/CodexInstallation.test.ts index 906d04bab699..a0d22decc0c2 100644 --- a/apps/server/src/provider/CodexInstallation.test.ts +++ b/apps/server/src/provider/CodexInstallation.test.ts @@ -1,5 +1,5 @@ import * as NodeServices from "@effect/platform-node/NodeServices"; -import { BUNDLED_MODEL_MANIFEST, ModelManifest, type ModelManifestData } from "./ModelManifest.ts"; +import * as ModelManifest from "./ModelManifest.ts"; import { expect, it } from "@effect/vitest"; import { HostProcessArchitecture, @@ -15,12 +15,7 @@ import * as Scope from "effect/Scope"; import * as Stream from "effect/Stream"; import { HttpClient, HttpClientResponse } from "effect/unstable/http"; import * as NodeCrypto from "node:crypto"; -import { - makeCodexInstallation, - type CodexInstallation, - type CodexInstallationOptions, - resolveCodexReleaseAsset, -} from "./CodexInstallation.ts"; +import * as CodexInstallation from "./CodexInstallation.ts"; const archive = Buffer.from( "H4sIAAAAAAAC/+3W0W6DIBQGYB/FcD0sKNSkD7J7aom6tmAQtzXL3n3QZM3qdWVt+n8XoCckJp78wLY3q8bu9Ge2HBbUUp7nYD4H8s9zrNdSsCxnWQLT6JULn8ye09K9h/u2/c0/jSM9xqGzo0+bf3Gdf15WQiD/Kdy61/CA+z8dlO9Wrv2387+c5Z9VZY38p7BY0+Gh8t/sVauLt9Ga9Pnnop7ln1e4/6fxRQ7qZCf/qt3YW0M2OX/JyfvljbCCy3XBSaiGH9VqH4tKuaZbC6qG4aDpTrmP3sQV2nh3Gmxvzqsud0vyjaABAAAAAAAAAAAAAAAk8gOq19rvACgAAA==", @@ -35,8 +30,8 @@ const asset = { }; const makeHarness = Effect.fn("test.makeCodexInstallation")(function* ( input: { - options?: Partial; - manifestCurrent?: Effect.Effect; + options?: Partial; + manifestCurrent?: Effect.Effect; body?: Stream.Stream; baseDir?: string; local?: { version: string; appServerFails?: boolean; versionFails?: boolean }; @@ -57,16 +52,16 @@ const makeHarness = Effect.fn("test.makeCodexInstallation")(function* ( ); } let downloads = 0; - const installation = yield* makeCodexInstallation({ + const installation = yield* CodexInstallation.makeCodexInstallation({ baseDir, releaseAsset: asset, validate: () => Effect.void, ...input.options, }).pipe( - Effect.provideService(ModelManifest, { - current: input.manifestCurrent ?? Effect.succeed(BUNDLED_MODEL_MANIFEST), - refresh: Effect.succeed(BUNDLED_MODEL_MANIFEST), - forceRefresh: Effect.succeed(BUNDLED_MODEL_MANIFEST), + Effect.provideService(ModelManifest.ModelManifest, { + current: input.manifestCurrent ?? Effect.succeed(ModelManifest.BUNDLED_MODEL_MANIFEST), + refresh: Effect.succeed(ModelManifest.BUNDLED_MODEL_MANIFEST), + forceRefresh: Effect.succeed(ModelManifest.BUNDLED_MODEL_MANIFEST), refreshInBackground: Effect.void, }), Effect.provideService(HostProcessPlatform, "darwin"), @@ -90,7 +85,7 @@ const makeHarness = Effect.fn("test.makeCodexInstallation")(function* ( ); return { installation, fs, baseDir, localBinaryPath, probeLog, downloads: () => downloads }; }); -const terminalState = (installation: CodexInstallation["Service"]) => +const terminalState = (installation: CodexInstallation.CodexInstallation["Service"]) => installation.changes.pipe( Stream.filter((state) => ["succeeded", "failed", "cancelled"].includes(state.phase)), Stream.runHead, @@ -183,7 +178,7 @@ it.effect("rechecks compatibility after the local executable is replaced", () => it.effect("rechecks a cached local executable when the shared manifest policy changes", () => Effect.gen(function* () { - let manifest = BUNDLED_MODEL_MANIFEST; + let manifest = ModelManifest.BUNDLED_MODEL_MANIFEST; const h = yield* makeHarness({ local: { version: "0.156.0" }, manifestCurrent: Effect.sync(() => manifest), @@ -217,7 +212,10 @@ it.effect("uses bundled Codex compatibility when the remote manifest omits its p Effect.gen(function* () { const h = yield* makeHarness({ local: { version: "0.156.0" }, - manifestCurrent: Effect.succeed({ ...BUNDLED_MODEL_MANIFEST, compatibility: [] }), + manifestCurrent: Effect.succeed({ + ...ModelManifest.BUNDLED_MODEL_MANIFEST, + compatibility: [], + }), }); expect((yield* h.installation.start).source).toBe("local"); expect(h.downloads()).toBe(0); @@ -361,6 +359,8 @@ it.effect("keeps an activated runtime when a later update fails verification", ( it("publishes complete packages for all supported platforms", () => { for (const platform of ["darwin", "linux", "win32"] as const) for (const arch of ["x64", "arm64"]) - expect(resolveCodexReleaseAsset(platform, arch)?.url).toContain("codex-package-"); - expect(resolveCodexReleaseAsset("linux", "riscv64")).toBeNull(); + expect(CodexInstallation.resolveCodexReleaseAsset(platform, arch)?.url).toContain( + "codex-package-", + ); + expect(CodexInstallation.resolveCodexReleaseAsset("linux", "riscv64")).toBeNull(); }); diff --git a/apps/server/src/provider/CodexInstallation.ts b/apps/server/src/provider/CodexInstallation.ts index 4beec81770a6..543122fe8ae1 100644 --- a/apps/server/src/provider/CodexInstallation.ts +++ b/apps/server/src/provider/CodexInstallation.ts @@ -25,8 +25,8 @@ import * as SubscriptionRef from "effect/SubscriptionRef"; import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import * as NodeCrypto from "node:crypto"; -import { ServerConfig } from "../config.ts"; -import { BUNDLED_MODEL_MANIFEST, ModelManifest } from "./ModelManifest.ts"; +import * as ServerConfig from "../config.ts"; +import * as ModelManifest from "./ModelManifest.ts"; import { resolveProviderCompatibility } from "./providerCompatibility.ts"; const DRIVER = ProviderDriverKind.make("codex"); @@ -144,7 +144,7 @@ export class CodexInstallation extends Context.Service< static readonly layer = Layer.effect( CodexInstallation, Effect.gen(function* () { - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; return yield* makeCodexInstallation({ baseDir: config.baseDir }); }), ); @@ -162,7 +162,7 @@ const isRunning = (state: ProviderInstallState) => export const makeCodexInstallation = Effect.fn("makeCodexInstallation")(function* ( options: CodexInstallationOptions, ) { - const manifestService = yield* ModelManifest; + const manifestService = yield* ModelManifest.ModelManifest; const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; const crypto = yield* Crypto.Crypto; @@ -266,7 +266,11 @@ export const makeCodexInstallation = Effect.fn("makeCodexInstallation")(function const manifest = yield* manifestService.current; return ( resolveProviderCompatibility(manifest.compatibility, DRIVER, version) ?? - resolveProviderCompatibility(BUNDLED_MODEL_MANIFEST.compatibility, DRIVER, version) + resolveProviderCompatibility( + ModelManifest.BUNDLED_MODEL_MANIFEST.compatibility, + DRIVER, + version, + ) ); }); const resolveManaged = Effect.fn("CodexInstallation.resolveManaged")( diff --git a/apps/server/src/provider/CodexManagedRuntime.test.ts b/apps/server/src/provider/CodexManagedRuntime.test.ts index aeba7c5bfe3d..33ccaa13ea8c 100644 --- a/apps/server/src/provider/CodexManagedRuntime.test.ts +++ b/apps/server/src/provider/CodexManagedRuntime.test.ts @@ -11,10 +11,10 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; import { HttpClient, HttpClientResponse } from "effect/unstable/http"; -import { ServerConfig } from "../config.ts"; -import { ServerSecretStore } from "../auth/ServerSecretStore.ts"; -import { ServerEnvironmentIdentity } from "../environment/ServerEnvironment.ts"; -import { CodexInstallation } from "./CodexInstallation.ts"; +import * as ServerConfig from "../config.ts"; +import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; +import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; +import * as CodexInstallation from "./CodexInstallation.ts"; import { makeCodexManagedRuntime } from "./CodexManagedRuntime.ts"; import * as ProviderCredentialStore from "./ProviderCredentialStore.ts"; import { codexAppServerArgs } from "./Layers/codexLaunchArgs.ts"; @@ -63,7 +63,7 @@ for (const source of ["managed", "local"] as const) yield* fs.writeFileString(path.join(sharedHome, "auth.json"), "native-auth-unchanged"); yield* fs.writeFileString(path.join(sharedHome, "config.toml"), "# shared config\n"); const data = new Map(); - const secrets = ServerSecretStore.of({ + const secrets = ServerSecretStore.ServerSecretStore.of({ get: (key) => Effect.sync(() => Option.fromUndefinedOr(data.get(key))), set: (key, value) => Effect.sync(() => { @@ -84,7 +84,7 @@ for (const source of ["managed", "local"] as const) source, version: "0.156.1", }; - const installerLayer = Layer.mock(CodexInstallation)({ + const installerLayer = Layer.mock(CodexInstallation.CodexInstallation)({ managedDirectory: "/isolated/tools/codex", resolve: () => Effect.succeed(executable), acquire: () => @@ -181,8 +181,8 @@ for (const source of ["managed", "local"] as const) "native-auth-unchanged", ); }).pipe( - Effect.provideService(ServerSecretStore, secrets), - Effect.provideService(ServerEnvironmentIdentity, { + Effect.provideService(ServerSecretStore.ServerSecretStore, secrets), + Effect.provideService(ServerEnvironment.ServerEnvironmentIdentity, { getEnvironmentId: Effect.succeed( EnvironmentId.make("00000000-0000-4000-8000-000000000001"), ), @@ -217,9 +217,9 @@ for (const source of ["managed", "local"] as const) }).pipe( Effect.scoped, Effect.provide( - ServerConfig.layerTest(process.cwd(), { prefix: "t3-managed-runtime-" }).pipe( - Layer.provideMerge(NodeServices.layer), - ), + ServerConfig.layerTest(process.cwd(), { + prefix: "t3-managed-runtime-", + }).pipe(Layer.provideMerge(NodeServices.layer)), ), ), ); diff --git a/apps/server/src/provider/CodexManagedRuntime.ts b/apps/server/src/provider/CodexManagedRuntime.ts index 528673fc3406..f21cc6c3bcf3 100644 --- a/apps/server/src/provider/CodexManagedRuntime.ts +++ b/apps/server/src/provider/CodexManagedRuntime.ts @@ -4,8 +4,8 @@ import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; -import { ServerConfig } from "../config.ts"; -import { CodexInstallation } from "./CodexInstallation.ts"; +import * as ServerConfig from "../config.ts"; +import * as CodexInstallation from "./CodexInstallation.ts"; import { makeCodexChatGptAuth } from "./CodexChatGptAuth.ts"; import { materializeCodexShadowHome } from "./Drivers/CodexHomeLayout.ts"; @@ -36,8 +36,8 @@ export const makeCodexManagedRuntime = Effect.fn("makeCodexManagedRuntime")(func readonly environment: NodeJS.ProcessEnv; readonly config: CodexSettings; }) { - const installation = yield* CodexInstallation; - const config = yield* ServerConfig; + const installation = yield* CodexInstallation.CodexInstallation; + const config = yield* ServerConfig.ServerConfig; const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; const auth = yield* makeCodexChatGptAuth({ diff --git a/apps/server/src/provider/CursorCredentialStore.test.ts b/apps/server/src/provider/CursorCredentialStore.test.ts index 5705fa01a03c..e3c0995ba072 100644 --- a/apps/server/src/provider/CursorCredentialStore.test.ts +++ b/apps/server/src/provider/CursorCredentialStore.test.ts @@ -6,14 +6,14 @@ import * as FileSystem from "effect/FileSystem"; import * as Path from "effect/Path"; import * as Option from "effect/Option"; -import { ServerSecretStore } from "../auth/ServerSecretStore.ts"; +import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; import { makeCursorCredentialStore } from "./CursorCredentialStore.ts"; const makeSecrets = () => { const data = new Map(); return { data, - secrets: ServerSecretStore.of({ + secrets: ServerSecretStore.ServerSecretStore.of({ get: (key) => Effect.sync(() => Option.fromUndefinedOr(data.get(key))), set: (key, bytes) => Effect.sync(() => { @@ -78,12 +78,12 @@ it.effect.each([ const instanceId = ProviderInstanceId.make("personal"); if (stored !== undefined) { const current = yield* makeCursorCredentialStore(instanceId).pipe( - Effect.provideService(ServerSecretStore, secrets), + Effect.provideService(ServerSecretStore.ServerSecretStore, secrets), ); yield* secrets.set(current.binding.key, new TextEncoder().encode(stored)); } const migrated = yield* makeCursorCredentialStore(instanceId, legacyFile).pipe( - Effect.provideService(ServerSecretStore, secrets), + Effect.provideService(ServerSecretStore.ServerSecretStore, secrets), ); assert.strictEqual((yield* Effect.tryPromise(() => migrated.store.load()))?.apiKey, expected); assert.isFalse(yield* fileSystem.exists(legacyFile)); @@ -95,7 +95,7 @@ it.effect( () => Effect.gen(function* () { const data = new Map(); - const secrets = ServerSecretStore.of({ + const secrets = ServerSecretStore.ServerSecretStore.of({ get: (key) => Effect.sync(() => Option.fromUndefinedOr(data.get(key))), set: (key, bytes) => Effect.sync(() => { @@ -110,7 +110,7 @@ it.effect( }); const makeStore = (id: string) => makeCursorCredentialStore(ProviderInstanceId.make(id)).pipe( - Effect.provideService(ServerSecretStore, secrets), + Effect.provideService(ServerSecretStore.ServerSecretStore, secrets), ); const personal = yield* makeStore("personal"); const work = yield* makeStore("work"); diff --git a/apps/server/src/provider/Drivers/AcpRegistryDriver.test.ts b/apps/server/src/provider/Drivers/AcpRegistryDriver.test.ts index a905a0861f06..289039cd0f2c 100644 --- a/apps/server/src/provider/Drivers/AcpRegistryDriver.test.ts +++ b/apps/server/src/provider/Drivers/AcpRegistryDriver.test.ts @@ -8,7 +8,7 @@ import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; -import { AcpRegistryCatalog, type AcpRegistryInspection } from "../acp/AcpRegistrySupport.ts"; +import * as AcpRegistrySupport from "../acp/AcpRegistrySupport.ts"; import { acpRegistrySnapshotReadiness, applyAcpRegistryAvailableCommands, @@ -34,7 +34,9 @@ const noSessionManagement = { canConfigureProviders: false, } as const; -function catalogWithInspection(inspection: AcpRegistryInspection): AcpRegistryCatalog["Service"] { +function catalogWithInspection( + inspection: AcpRegistrySupport.AcpRegistryInspection, +): AcpRegistrySupport.AcpRegistryCatalog["Service"] { return { search: () => Effect.die("unused search"), prepare: () => Effect.die("unused prepare"), @@ -386,7 +388,7 @@ describe("acpRegistrySnapshotReadiness", () => { }), ).pipe( Effect.provideService( - AcpRegistryCatalog, + AcpRegistrySupport.AcpRegistryCatalog, catalogWithInspection({ status: "ready", agentId: "test-agent", @@ -414,7 +416,7 @@ describe("acpRegistrySnapshotReadiness", () => { environment: { PATH: "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/provider/bin" }, }).pipe( Effect.provideService( - AcpRegistryCatalog, + AcpRegistrySupport.AcpRegistryCatalog, catalogWithInspection({ status: "ready", agentId: "test-agent", diff --git a/apps/server/src/provider/Drivers/AcpRegistryDriver.ts b/apps/server/src/provider/Drivers/AcpRegistryDriver.ts index 37337b2036e4..68bf797fa084 100644 --- a/apps/server/src/provider/Drivers/AcpRegistryDriver.ts +++ b/apps/server/src/provider/Drivers/AcpRegistryDriver.ts @@ -22,12 +22,12 @@ import * as Semaphore from "effect/Semaphore"; import { ChildProcessSpawner } from "effect/unstable/process"; import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import { AcpRegistryAdapterV2Driver, type AcpRegistryAdapterV2DriverEnv, } from "../../orchestration-v2/Adapters/AcpRegistryAdapterV2.ts"; -import { ServerSettingsService } from "../../serverSettings.ts"; +import * as ServerSettings from "../../serverSettings.ts"; import type { TextGeneration } from "../../textGeneration/TextGeneration.ts"; import { ProviderDriverError } from "../Errors.ts"; import { makeManagedServerProvider } from "../makeManagedServerProvider.ts"; @@ -57,8 +57,8 @@ import { probeAcpRegistryConfiguration, setAcpRegistryProvider, } from "../acp/AcpRegistryProbe.ts"; -import { AcpRegistryCatalog, type AcpRegistryInspection } from "../acp/AcpRegistrySupport.ts"; -import { AcpRegistryRuntimeCoordinator } from "../acp/AcpRegistryRuntimeCoordinator.ts"; +import * as AcpRegistrySupport from "../acp/AcpRegistrySupport.ts"; +import * as AcpRegistryRuntimeCoordinator from "../acp/AcpRegistryRuntimeCoordinator.ts"; import * as AcpRegistryAuth from "../acp/AcpRegistryAuth.ts"; import * as AcpRegistryAuthenticationState from "../acp/AcpRegistryAuthenticationState.ts"; @@ -126,7 +126,9 @@ function modelsFromDiscovery( } export function acpRegistrySnapshotReadiness( - inspection: AcpRegistryInspection | { readonly status: "failed"; readonly message: string }, + inspection: + | AcpRegistrySupport.AcpRegistryInspection + | { readonly status: "failed"; readonly message: string }, ): Pick { switch (inspection.status) { case "ready": @@ -302,7 +304,7 @@ export function buildCheckedAcpRegistrySnapshot( readonly settings: AcpRegistrySettings; readonly checkedAt: string; readonly inspection: - | AcpRegistryInspection + | AcpRegistrySupport.AcpRegistryInspection | { readonly status: "failed"; readonly message: string }; readonly probe?: AcpRegistryConfigurationProbeResult; readonly probeError?: AcpRegistryOperationError; @@ -368,12 +370,12 @@ export const checkAcpRegistryProviderStatus = Effect.fn("AcpRegistryDriver.check readonly environment: NodeJS.ProcessEnv; }, probeConfiguration: AcpRegistryConfigurationProbe, - ): Effect.fn.Return { + ): Effect.fn.Return { const checkedAt = DateTime.formatIso(yield* DateTime.now); if (!input.settings.enabled) { return yield* buildInitialAcpRegistrySnapshot(input); } - const catalog = yield* AcpRegistryCatalog; + const catalog = yield* AcpRegistrySupport.AcpRegistryCatalog; const inspected = yield* Effect.result(catalog.inspect(input.settings, input.environment)); if (Result.isFailure(inspected)) { return buildCheckedAcpRegistrySnapshot({ @@ -424,12 +426,12 @@ export const checkAcpRegistryProviderReadiness = Effect.fn( readonly settings: AcpRegistrySettings; readonly environment: NodeJS.ProcessEnv; }, -): Effect.fn.Return { +): Effect.fn.Return { const checkedAt = DateTime.formatIso(yield* DateTime.now); if (!input.settings.enabled) { return yield* buildInitialAcpRegistrySnapshot(input); } - const catalog = yield* AcpRegistryCatalog; + const catalog = yield* AcpRegistrySupport.AcpRegistryCatalog; const inspected = yield* Effect.result(catalog.inspect(input.settings, input.environment)); const snapshot = buildCheckedAcpRegistrySnapshot({ ...input, @@ -452,7 +454,7 @@ export const checkAcpRegistryProviderReadiness = Effect.fn( export type AcpRegistryDriverEnv = | AcpRegistryAdapterV2DriverEnv | BackgroundPolicy.BackgroundPolicy - | ServerSettingsService; + | ServerSettings.ServerSettingsService; /** Canonical provider-instance wrapper for ACP Registry orchestration adapters. */ export const AcpRegistryDriver: ProviderDriver = { @@ -465,8 +467,10 @@ export const AcpRegistryDriver: ProviderDriver decodeSettings({}), create: ({ instanceId, displayName, accentColor, environment, enabled, config }) => Effect.gen(function* () { - const catalog = yield* AcpRegistryCatalog; - const runtimeCoordinator = yield* Effect.serviceOption(AcpRegistryRuntimeCoordinator); + const catalog = yield* AcpRegistrySupport.AcpRegistryCatalog; + const runtimeCoordinator = yield* Effect.serviceOption( + AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator, + ); if (Option.isSome(runtimeCoordinator)) { yield* runtimeCoordinator.value.clearAvailableCommands(instanceId); yield* runtimeCoordinator.value.clearLiveConfiguration(instanceId); @@ -479,8 +483,8 @@ export const AcpRegistryDriver: ProviderDriver, ): Effect.Effect => { const provided = effect.pipe( - Effect.provideService(AcpRegistryCatalog, catalog), + Effect.provideService(AcpRegistrySupport.AcpRegistryCatalog, catalog), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.provideService(Crypto.Crypto, crypto), ); return Option.isSome(runtimeCoordinator) ? provided.pipe( - Effect.provideService(AcpRegistryRuntimeCoordinator, runtimeCoordinator.value), + Effect.provideService( + AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator, + runtimeCoordinator.value, + ), ) : provided; }; @@ -763,7 +772,7 @@ export const AcpRegistryDriver: ProviderDriver confirmedAuthentication.set(false)), @@ -878,7 +887,10 @@ export const AcpRegistryDriver: ProviderDriver = []; - const installation = Layer.mock(AntigravityInstallation)({ + const installation = Layer.mock(AntigravityInstallation.AntigravityInstallation)({ managedDirectory: root, resolve: () => Effect.gen(function* () { if (controls.failResolution) { - return yield* new AntigravityInstallationError({ + return yield* new AntigravityInstallation.AntigravityInstallationError({ operation: "resolve", detail: "Fixture resolution failed.", }); @@ -148,7 +144,7 @@ const makeHarness = Effect.fn("makeAntigravityDriverHarness")(function* ( acquisitions.push({ binaryPath, path: environment?.PATH }); yield* controls.beforeAcquire; if (controls.failResolution) { - return yield* new AntigravityInstallationError({ + return yield* new AntigravityInstallation.AntigravityInstallationError({ operation: "resolve", detail: "Fixture resolution failed.", }); @@ -252,15 +248,20 @@ const testLayer = ServerConfig.layerTest(process.cwd(), { prefix: "t3-antigravity-driver-config-", }).pipe( Layer.provideMerge(NodeServices.layer), - Layer.provideMerge(ServerSettingsService.layerTest()), + Layer.provideMerge(ServerSettings.layerTest()), Layer.provideMerge( Layer.mock(BackgroundPolicy.BackgroundPolicy)({ shouldRunScopeWork: () => Effect.succeed(false), }), ), - Layer.provideMerge(Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers)), + Layer.provideMerge( + Layer.succeed( + ProviderEventLoggers.ProviderEventLoggers, + ProviderEventLoggers.NoOpProviderEventLoggers, + ), + ), Layer.provideMerge(ModelManifest.layerTest), - Layer.provideMerge(idAllocatorLayer), + Layer.provideMerge(IdAllocator.layer), ); it.layer(testLayer)("AntigravityDriver", (it) => { @@ -499,7 +500,7 @@ it.layer(testLayer)("AntigravityDriver", (it) => { Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; const instanceId = ProviderInstanceId.make("antigravity-orphan-sweep"); const directories = yield* resolveAntigravityInstanceDirectories( config.stateDir, @@ -520,7 +521,7 @@ it.layer(testLayer)("AntigravityDriver", (it) => { environment: [], }).pipe( Effect.provide( - Layer.mock(AntigravityInstallation)({ + Layer.mock(AntigravityInstallation.AntigravityInstallation)({ managedDirectory: config.stateDir, resolve: () => Effect.die("unused"), acquire: () => Effect.die("unused"), diff --git a/apps/server/src/provider/Drivers/AntigravityDriver.ts b/apps/server/src/provider/Drivers/AntigravityDriver.ts index 4e1a5023937c..cabcc643006e 100644 --- a/apps/server/src/provider/Drivers/AntigravityDriver.ts +++ b/apps/server/src/provider/Drivers/AntigravityDriver.ts @@ -19,14 +19,14 @@ import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawne import type { AcpError } from "effect-acp/errors"; import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; -import { ServerConfig } from "../../config.ts"; -import { ServerSettingsService } from "../../serverSettings.ts"; +import * as ServerConfig from "../../config.ts"; +import * as ServerSettings from "../../serverSettings.ts"; import { isAntigravityTextGenerationAvailable, makeAntigravityTextGeneration, } from "../../textGeneration/AntigravityTextGeneration.ts"; import { makeAntigravityAuth, type AntigravityAuth } from "../AntigravityAuth.ts"; -import { AntigravityInstallation } from "../AntigravityInstallation.ts"; +import * as AntigravityInstallation from "../AntigravityInstallation.ts"; import { antigravityAuthConfigIssue, antigravityAuthLabel, @@ -47,13 +47,13 @@ import { removeAntigravityRuntimeTempDirs, removeAntigravitySessionFiles, } from "../acp/AntigravitySessionFiles.ts"; -import { IdAllocatorV2 } from "../../orchestration-v2/IdAllocator.ts"; -import { ProviderContinuationRequests } from "../../orchestration-v2/ProviderContinuationRequests.ts"; +import * as IdAllocator from "../../orchestration-v2/IdAllocator.ts"; +import * as ProviderContinuationRequests from "../../orchestration-v2/ProviderContinuationRequests.ts"; import { makeAntigravityAdapterV2 } from "../../orchestration-v2/Adapters/AntigravityAdapterV2.ts"; import { makeAcpNativeLoggerFactory } from "../acp/AcpNativeLogging.ts"; import { ProviderDriverError } from "../Errors.ts"; import { makeAntigravityProvider } from "../Layers/AntigravityProvider.ts"; -import { ProviderEventLoggers } from "../Layers/ProviderEventLoggers.ts"; +import * as ProviderEventLoggers from "../Layers/ProviderEventLoggers.ts"; import * as ModelManifest from "../ModelManifest.ts"; import { defaultProviderContinuationIdentity, @@ -69,17 +69,17 @@ const decodeSettings = Schema.decodeSync(AntigravitySettings); const isNodeRuntimeUnavailableError = Schema.is(NodeRuntimeUnavailableError); export type AntigravityDriverEnv = - | AntigravityInstallation + | AntigravityInstallation.AntigravityInstallation | BackgroundPolicy.BackgroundPolicy | ChildProcessSpawner.ChildProcessSpawner | Crypto.Crypto | FileSystem.FileSystem - | IdAllocatorV2 + | IdAllocator.IdAllocatorV2 | ModelManifest.ModelManifest | Path.Path - | ProviderEventLoggers - | ServerConfig - | ServerSettingsService; + | ProviderEventLoggers.ProviderEventLoggers + | ServerConfig.ServerConfig + | ServerSettings.ServerSettingsService; /** Each instance owns its Google profile. Executable releases are shared by the environment. */ export const AntigravityDriver: ProviderDriver = { @@ -93,13 +93,13 @@ export const AntigravityDriver: ProviderDriver = { driverKind: DRIVER_KIND, @@ -120,10 +120,10 @@ export const ClaudeDriver: ProviderDriver = { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; - const { cwd } = yield* ServerConfig; + const { cwd } = yield* ServerConfig.ServerConfig; const httpClient = yield* HttpClient.HttpClient; const resetCreditCoordinator = yield* ResetCreditCoordinator.ResetCreditCoordinator; - const serverSettings = yield* ServerSettingsService; + const serverSettings = yield* ServerSettings.ServerSettingsService; const modelManifest = yield* ModelManifest.ModelManifest; const modelCatalog = modelManifest.current.pipe(Effect.map(resolveClaudeModelCatalog)); const processEnv = mergeProviderInstanceEnvironment(environment); diff --git a/apps/server/src/provider/Drivers/CodexDriver.test.ts b/apps/server/src/provider/Drivers/CodexDriver.test.ts index 62961b44020f..4d1a4ce3519f 100644 --- a/apps/server/src/provider/Drivers/CodexDriver.test.ts +++ b/apps/server/src/provider/Drivers/CodexDriver.test.ts @@ -1,6 +1,6 @@ -import { CodexInstallation } from "../CodexInstallation.ts"; -import { ServerSecretStore } from "../../auth/ServerSecretStore.ts"; -import { ServerEnvironmentIdentity } from "../../environment/ServerEnvironment.ts"; +import * as CodexInstallation from "../CodexInstallation.ts"; +import * as ServerSecretStore from "../../auth/ServerSecretStore.ts"; +import * as ServerEnvironment from "../../environment/ServerEnvironment.ts"; // @effect-diagnostics nodeBuiltinImport:off import * as NodeServices from "@effect/platform-node/NodeServices"; import * as NodeOS from "node:os"; @@ -23,10 +23,10 @@ import * as ChildProcess from "effect/unstable/process/ChildProcess"; import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner"; import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; -import { ServerConfig } from "../../config.ts"; -import { ServerSettingsService } from "../../serverSettings.ts"; +import * as ServerConfig from "../../config.ts"; +import * as ServerSettings from "../../serverSettings.ts"; import * as ResetCreditCoordinator from "../Layers/resetCreditCoordinator.ts"; -import { NoOpProviderEventLoggers, ProviderEventLoggers } from "../Layers/ProviderEventLoggers.ts"; +import * as ProviderEventLoggers from "../Layers/ProviderEventLoggers.ts"; import * as ModelManifest from "../ModelManifest.ts"; import { createProviderVersionAdvisory, @@ -34,11 +34,8 @@ import { resolveLatestProviderVersion, } from "../providerMaintenance.ts"; import { CodexDriver } from "./CodexDriver.ts"; -import { - CodexAppServerClientFactory, - type CodexAppServerClientFactoryShape, -} from "../../orchestration-v2/Adapters/CodexAdapterV2.ts"; -import { layer as idAllocatorLayer } from "../../orchestration-v2/IdAllocator.ts"; +import * as CodexAdapterV2 from "../../orchestration-v2/Adapters/CodexAdapterV2.ts"; +import * as IdAllocator from "../../orchestration-v2/IdAllocator.ts"; import { ProviderAdapterV2RuntimePolicy } from "../../orchestration-v2/ProviderAdapter.ts"; import * as ProviderCredentialStore from "../ProviderCredentialStore.ts"; @@ -46,22 +43,24 @@ const testLayer = ServerConfig.layerTest(process.cwd(), { prefix: "t3-codex-driver-maintenance-", }).pipe( Layer.provideMerge(NodeServices.layer), - Layer.provideMerge(idAllocatorLayer), + Layer.provideMerge(IdAllocator.layer), Layer.provideMerge( - Layer.mock(CodexAppServerClientFactory)({ + Layer.mock(CodexAdapterV2.CodexAppServerClientFactory)({ open: () => Effect.die("Maintenance resolution must not open a Codex session"), }), ), Layer.provideMerge( - Layer.mock(CodexInstallation)({ managedDirectory: "unused-managed-installation" }), + Layer.mock(CodexInstallation.CodexInstallation)({ + managedDirectory: "unused-managed-installation", + }), ), - Layer.provideMerge(Layer.mock(ServerSecretStore)({})), + Layer.provideMerge(Layer.mock(ServerSecretStore.ServerSecretStore)({})), Layer.provideMerge( - Layer.succeed(ServerEnvironmentIdentity, { + Layer.succeed(ServerEnvironment.ServerEnvironmentIdentity, { getEnvironmentId: Effect.succeed(EnvironmentId.make("00000000-0000-4000-8000-000000000001")), }), ), - Layer.provideMerge(ServerSettingsService.layerTest()), + Layer.provideMerge(ServerSettings.layerTest()), Layer.provideMerge(ModelManifest.layerTest), Layer.provideMerge(ResetCreditCoordinator.layerTest), Layer.provideMerge( @@ -69,7 +68,12 @@ const testLayer = ServerConfig.layerTest(process.cwd(), { shouldRunScopeWork: () => Effect.succeed(false), }), ), - Layer.provideMerge(Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers)), + Layer.provideMerge( + Layer.succeed( + ProviderEventLoggers.ProviderEventLoggers, + ProviderEventLoggers.NoOpProviderEventLoggers, + ), + ), Layer.provideMerge( Layer.succeed( HttpClient.HttpClient, @@ -91,7 +95,7 @@ it.layer(testLayer)("CodexDriver", (it) => { Effect.gen(function* () { const instanceId = ProviderInstanceId.make("restored-managed-account"); const credentials = new Map(); - const secrets = ServerSecretStore.of({ + const secrets = ServerSecretStore.ServerSecretStore.of({ get: (key) => Effect.sync(() => Option.fromUndefinedOr(credentials.get(key))), set: (key, value) => Effect.sync(() => { @@ -123,10 +127,12 @@ it.layer(testLayer)("CodexDriver", (it) => { source: "local" as const, version: "0.156.1", }; - const installation = yield* CodexInstallation; - const serverConfig = yield* ServerConfig; + const installation = yield* CodexInstallation.CodexInstallation; + const serverConfig = yield* ServerConfig.ServerConfig; const sharedHome = NodePath.join(serverConfig.stateDir, "shared-codex-home"); - const launches: Array[0]> = []; + const launches: Array< + Parameters[0] + > = []; // Sign-out interrupts an account check that is still resolving the runtime. // Interrupt the two startup checks that way (one is the sign-in listener's); // the disconnect below must still refresh. @@ -150,8 +156,8 @@ it.layer(testLayer)("CodexDriver", (it) => { config: { ...CodexDriver.defaultConfig(), setupMode: "managed", homePath: sharedHome }, }).pipe( Effect.provideService( - CodexAppServerClientFactory, - CodexAppServerClientFactory.of({ + CodexAdapterV2.CodexAppServerClientFactory, + CodexAdapterV2.CodexAppServerClientFactory.of({ open: (launch) => Effect.sync(() => launches.push(launch)).pipe( Effect.andThen(Effect.die("The fixture stops after recording the launch")), @@ -159,8 +165,8 @@ it.layer(testLayer)("CodexDriver", (it) => { }), ), Effect.provideService( - CodexInstallation, - CodexInstallation.of({ + CodexInstallation.CodexInstallation, + CodexInstallation.CodexInstallation.of({ ...installation, managedDirectory: "unused-managed-installation", resolve: () => Effect.succeed(executable), @@ -230,7 +236,7 @@ it.layer(testLayer)("CodexDriver", (it) => { expect(after.models).toEqual([]); expect(Option.isNone(yield* store.get)).toBe(true); }).pipe( - Effect.provideService(ServerSecretStore, secrets), + Effect.provideService(ServerSecretStore.ServerSecretStore, secrets), Effect.provideService( HttpClient.HttpClient, HttpClient.make((request) => diff --git a/apps/server/src/provider/Drivers/CodexDriver.ts b/apps/server/src/provider/Drivers/CodexDriver.ts index 4e81275e9d94..20a00064a343 100644 --- a/apps/server/src/provider/Drivers/CodexDriver.ts +++ b/apps/server/src/provider/Drivers/CodexDriver.ts @@ -32,14 +32,14 @@ import { ChildProcessSpawner } from "effect/unstable/process"; import { makeCodexTextGeneration } from "../../textGeneration/CodexTextGeneration.ts"; import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import { expandHomePath } from "../../pathExpansion.ts"; -import { ProviderEventLoggers } from "../Layers/ProviderEventLoggers.ts"; +import * as ProviderEventLoggers from "../Layers/ProviderEventLoggers.ts"; import { createCodexAdapterV2, type CodexAdapterV2DriverEnv, } from "../../orchestration-v2/Adapters/CodexAdapterV2.ts"; -import { ServerSettingsService } from "../../serverSettings.ts"; +import * as ServerSettings from "../../serverSettings.ts"; import { ProviderDriverError } from "../Errors.ts"; import * as ResetCreditCoordinator from "../Layers/resetCreditCoordinator.ts"; import { @@ -72,9 +72,9 @@ import { resolveCodexHomeLayout, } from "./CodexHomeLayout.ts"; import { makeManagedCodexProvider } from "./CodexManagedProvider.ts"; -import { CodexInstallation } from "../CodexInstallation.ts"; -import { ServerSecretStore } from "../../auth/ServerSecretStore.ts"; -import { ServerEnvironmentIdentity } from "../../environment/ServerEnvironment.ts"; +import * as CodexInstallation from "../CodexInstallation.ts"; +import * as ServerSecretStore from "../../auth/ServerSecretStore.ts"; +import * as ServerEnvironment from "../../environment/ServerEnvironment.ts"; const decodeCodexSettings = Schema.decodeSync(CodexSettings); const DRIVER_KIND = ProviderDriverKind.make("codex"); @@ -117,12 +117,12 @@ export type CodexDriverEnv = | HttpClient.HttpClient | ModelManifest.ModelManifest | Path.Path - | ProviderEventLoggers - | ServerConfig - | ServerSettingsService - | ServerSecretStore - | ServerEnvironmentIdentity - | CodexInstallation; + | ProviderEventLoggers.ProviderEventLoggers + | ServerConfig.ServerConfig + | ServerSettings.ServerSettingsService + | ServerSecretStore.ServerSecretStore + | ServerEnvironment.ServerEnvironmentIdentity + | CodexInstallation.CodexInstallation; export const CodexDriver: ProviderDriver = { driverKind: DRIVER_KIND, @@ -148,7 +148,7 @@ export const CodexDriver: ProviderDriver = { const fileSystem = yield* FileSystem.FileSystem; const pathService = yield* Path.Path; const httpClient = yield* HttpClient.HttpClient; - const serverSettings = yield* ServerSettingsService; + const serverSettings = yield* ServerSettings.ServerSettingsService; const modelManifest = yield* ModelManifest.ModelManifest; const processEnv = mergeProviderInstanceEnvironment(environment); const homeLayout = yield* resolveCodexHomeLayout(config); diff --git a/apps/server/src/provider/Drivers/CursorDriver.test.ts b/apps/server/src/provider/Drivers/CursorDriver.test.ts index ddc71a3a3210..78bafdabc5bf 100644 --- a/apps/server/src/provider/Drivers/CursorDriver.test.ts +++ b/apps/server/src/provider/Drivers/CursorDriver.test.ts @@ -12,33 +12,40 @@ import { HttpClient } from "effect/unstable/http"; import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner"; import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; -import { ServerConfig } from "../../config.ts"; -import { ServerSettingsService } from "../../serverSettings.ts"; -import { NoOpProviderEventLoggers, ProviderEventLoggers } from "../Layers/ProviderEventLoggers.ts"; +import * as ServerConfig from "../../config.ts"; +import * as ServerSettings from "../../serverSettings.ts"; +import * as ProviderEventLoggers from "../Layers/ProviderEventLoggers.ts"; import { CursorDriver } from "./CursorDriver.ts"; -import { CursorAgentSdkRunner } from "../../orchestration-v2/Adapters/CursorAgentSdk.ts"; -import { layer as idAllocatorLayer } from "../../orchestration-v2/IdAllocator.ts"; +import * as CursorAgentSdk from "../../orchestration-v2/Adapters/CursorAgentSdk.ts"; +import * as IdAllocator from "../../orchestration-v2/IdAllocator.ts"; import { ProviderAdapterV2RuntimePolicy } from "../../orchestration-v2/ProviderAdapter.ts"; import { Cursor } from "../cursorSdk.ts"; const testLayer = ServerSecretStore.layer.pipe( Layer.provideMerge( - ServerConfig.layerTest(process.cwd(), { prefix: "t3-cursor-driver-copy-command-" }), + ServerConfig.layerTest(process.cwd(), { + prefix: "t3-cursor-driver-copy-command-", + }), ), Layer.provideMerge(NodeServices.layer), - Layer.provideMerge(idAllocatorLayer), + Layer.provideMerge(IdAllocator.layer), Layer.provideMerge( - Layer.mock(CursorAgentSdkRunner)({ + Layer.mock(CursorAgentSdk.CursorAgentSdkRunner)({ open: () => Effect.die("Maintenance resolution must not open a Cursor session"), }), ), - Layer.provideMerge(ServerSettingsService.layerTest()), + Layer.provideMerge(ServerSettings.layerTest()), Layer.provideMerge( Layer.mock(BackgroundPolicy.BackgroundPolicy)({ shouldRunScopeWork: () => Effect.succeed(false), }), ), - Layer.provideMerge(Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers)), + Layer.provideMerge( + Layer.succeed( + ProviderEventLoggers.ProviderEventLoggers, + ProviderEventLoggers.NoOpProviderEventLoggers, + ), + ), Layer.provideMerge( Layer.succeed( HttpClient.HttpClient, @@ -89,7 +96,7 @@ it.layer(testLayer)("CursorDriver", (it) => { const openedKeys: Array = []; let closed = 0; const instance = yield* CursorDriver.create(input).pipe( - Effect.provideService(CursorAgentSdkRunner, { + Effect.provideService(CursorAgentSdk.CursorAgentSdkRunner, { assertComplete: Effect.void, open: (request) => Effect.sync(() => { diff --git a/apps/server/src/provider/Drivers/CursorDriver.ts b/apps/server/src/provider/Drivers/CursorDriver.ts index 63b3dbad06c9..116ae062c93c 100644 --- a/apps/server/src/provider/Drivers/CursorDriver.ts +++ b/apps/server/src/provider/Drivers/CursorDriver.ts @@ -16,8 +16,8 @@ import { HttpClient } from "effect/unstable/http"; import { readCursorUsageLimits } from "../Layers/cursorUsageLimits.ts"; import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; -import { ServerConfig } from "../../config.ts"; -import { ServerSettingsService } from "../../serverSettings.ts"; +import * as ServerConfig from "../../config.ts"; +import * as ServerSettings from "../../serverSettings.ts"; import { makeCursorTextGeneration } from "../../textGeneration/CursorTextGeneration.ts"; import { CursorAdapterV2Driver, @@ -28,7 +28,7 @@ import { buildInitialCursorProviderSnapshot, checkCursorProviderStatus, } from "../Layers/CursorProvider.ts"; -import { CursorSdkCatalogLive } from "../Layers/CursorSdkCatalog.ts"; +import * as CursorSdkCatalog from "../Layers/CursorSdkCatalog.ts"; import { makeManagedServerProvider } from "../makeManagedServerProvider.ts"; import { defaultProviderContinuationIdentity, @@ -64,9 +64,9 @@ export type CursorDriverEnv = | Path.Path | HttpClient.HttpClient | BackgroundPolicy.BackgroundPolicy - | ServerConfig + | ServerConfig.ServerConfig | ServerSecretStore.ServerSecretStore - | ServerSettingsService; + | ServerSettings.ServerSettingsService; export const CursorDriver: ProviderDriver = { driverKind: DRIVER_KIND, @@ -78,7 +78,7 @@ export const CursorDriver: ProviderDriver = { defaultConfig: (): CursorSettings => decodeCursorSettings({}), create: ({ instanceId, displayName, accentColor, environment, enabled, config }) => Effect.gen(function* () { - const serverSettings = yield* ServerSettingsService; + const serverSettings = yield* ServerSettings.ServerSettingsService; const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const httpClient = yield* HttpClient.HttpClient; @@ -99,7 +99,7 @@ export const CursorDriver: ProviderDriver = { const credentials = yield* CursorCredentialStore.makeCursorCredentialStore( instanceId, path.join( - (yield* ServerConfig).stateDir, + (yield* ServerConfig.ServerConfig).stateDir, "provider-auth", encodeURIComponent(instanceId), "cursor.json", @@ -231,7 +231,7 @@ export const CursorDriver: ProviderDriver = { Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path), Effect.map(stampSnapshot), - Effect.provide(CursorSdkCatalogLive), + Effect.provide(CursorSdkCatalog.CursorSdkCatalogLive), ); const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings); diff --git a/apps/server/src/provider/Drivers/GrokDriver.test.ts b/apps/server/src/provider/Drivers/GrokDriver.test.ts index 11034469acac..a9d6791a3e50 100644 --- a/apps/server/src/provider/Drivers/GrokDriver.test.ts +++ b/apps/server/src/provider/Drivers/GrokDriver.test.ts @@ -10,9 +10,9 @@ import { HttpClient } from "effect/unstable/http"; import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner"; import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; -import { ServerConfig } from "../../config.ts"; -import { ServerSettingsService } from "../../serverSettings.ts"; -import { NoOpProviderEventLoggers, ProviderEventLoggers } from "../Layers/ProviderEventLoggers.ts"; +import * as ServerConfig from "../../config.ts"; +import * as ServerSettings from "../../serverSettings.ts"; +import * as ProviderEventLoggers from "../Layers/ProviderEventLoggers.ts"; import { GrokDriver } from "./GrokDriver.ts"; import * as IdAllocator from "../../orchestration-v2/IdAllocator.ts"; @@ -22,13 +22,18 @@ const testLayer = ServerConfig.layerTest(process.cwd(), { }).pipe( Layer.provideMerge(NodeServices.layer), Layer.provideMerge(IdAllocator.layer), - Layer.provideMerge(ServerSettingsService.layerTest()), + Layer.provideMerge(ServerSettings.layerTest()), Layer.provideMerge( Layer.mock(BackgroundPolicy.BackgroundPolicy)({ shouldRunScopeWork: () => Effect.succeed(false), }), ), - Layer.provideMerge(Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers)), + Layer.provideMerge( + Layer.succeed( + ProviderEventLoggers.ProviderEventLoggers, + ProviderEventLoggers.NoOpProviderEventLoggers, + ), + ), Layer.provideMerge( Layer.succeed( HttpClient.HttpClient, diff --git a/apps/server/src/provider/Drivers/GrokDriver.ts b/apps/server/src/provider/Drivers/GrokDriver.ts index 2bcdf9cf77bf..c056a740cfac 100644 --- a/apps/server/src/provider/Drivers/GrokDriver.ts +++ b/apps/server/src/provider/Drivers/GrokDriver.ts @@ -8,9 +8,9 @@ import { HttpClient } from "effect/unstable/http"; import { ChildProcessSpawner } from "effect/unstable/process"; import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; -import { ServerConfig } from "../../config.ts"; -import { ProviderEventLoggers } from "../Layers/ProviderEventLoggers.ts"; -import { ServerSettingsService } from "../../serverSettings.ts"; +import * as ServerConfig from "../../config.ts"; +import * as ProviderEventLoggers from "../Layers/ProviderEventLoggers.ts"; +import * as ServerSettings from "../../serverSettings.ts"; import { makeGrokTextGeneration } from "../../textGeneration/GrokTextGeneration.ts"; import { GrokAdapterV2Driver, @@ -82,9 +82,9 @@ export type GrokDriverEnv = | FileSystem.FileSystem | HttpClient.HttpClient | Path.Path - | ProviderEventLoggers - | ServerConfig - | ServerSettingsService; + | ProviderEventLoggers.ProviderEventLoggers + | ServerConfig.ServerConfig + | ServerSettings.ServerSettingsService; export const GrokDriver: ProviderDriver = { driverKind: DRIVER_KIND, @@ -101,8 +101,8 @@ export const GrokDriver: ProviderDriver = { const httpClient = yield* HttpClient.HttpClient; const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; - const serverSettings = yield* ServerSettingsService; - const { cwd } = yield* ServerConfig; + const serverSettings = yield* ServerSettings.ServerSettingsService; + const { cwd } = yield* ServerConfig.ServerConfig; const processEnv = mergeProviderInstanceEnvironment(environment); const continuationIdentity = defaultProviderContinuationIdentity({ driverKind: DRIVER_KIND, diff --git a/apps/server/src/provider/Drivers/PiDriver.ts b/apps/server/src/provider/Drivers/PiDriver.ts index 72cb0fec732b..075f9a76946f 100644 --- a/apps/server/src/provider/Drivers/PiDriver.ts +++ b/apps/server/src/provider/Drivers/PiDriver.ts @@ -15,8 +15,8 @@ import { HttpClient } from "effect/unstable/http"; import { ChildProcessSpawner } from "effect/unstable/process"; import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; -import { ServerConfig } from "../../config.ts"; -import { ServerSettingsService } from "../../serverSettings.ts"; +import * as ServerConfig from "../../config.ts"; +import * as ServerSettings from "../../serverSettings.ts"; import { makePiTextGeneration } from "../../textGeneration/PiTextGeneration.ts"; import { PiAdapterV2Driver, @@ -63,8 +63,8 @@ export type PiDriverEnv = | FileSystem.FileSystem | HttpClient.HttpClient | Path.Path - | ServerConfig - | ServerSettingsService; + | ServerConfig.ServerConfig + | ServerSettings.ServerSettingsService; const withInstanceIdentity = (input: { @@ -96,8 +96,8 @@ export const PiDriver: ProviderDriver = { const fileSystem = yield* FileSystem.FileSystem; const pathService = yield* Path.Path; const httpClient = yield* HttpClient.HttpClient; - const { cwd } = yield* ServerConfig; - const serverSettings = yield* ServerSettingsService; + const { cwd } = yield* ServerConfig.ServerConfig; + const serverSettings = yield* ServerSettings.ServerSettingsService; const processEnv = mergeProviderInstanceEnvironment(environment); const continuationIdentity = defaultProviderContinuationIdentity({ driverKind: DRIVER_KIND, diff --git a/apps/server/src/provider/Layers/AcpRegistryCatalog.ts b/apps/server/src/provider/Layers/AcpRegistryCatalog.ts index 7314b9a71f3c..fea9874cfaa9 100644 --- a/apps/server/src/provider/Layers/AcpRegistryCatalog.ts +++ b/apps/server/src/provider/Layers/AcpRegistryCatalog.ts @@ -2,21 +2,21 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Path from "effect/Path"; -import { ServerConfig } from "../../config.ts"; -import { AcpRegistryCatalog } from "../acp/AcpRegistrySupport.ts"; -import { AcpRegistryRuntimeCoordinator } from "../acp/AcpRegistryRuntimeCoordinator.ts"; +import * as ServerConfig from "../../config.ts"; +import * as AcpRegistrySupport from "../acp/AcpRegistrySupport.ts"; +import * as AcpRegistryRuntimeCoordinator from "../acp/AcpRegistryRuntimeCoordinator.ts"; /** Server-lifetime ACP Registry catalog shared by setup, snapshots, and turn launch. */ export const AcpRegistryCatalogLive = Layer.merge( Layer.unwrap( Effect.gen(function* () { - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; const path = yield* Path.Path; - return AcpRegistryCatalog.layer({ + return AcpRegistrySupport.AcpRegistryCatalog.layer({ cacheDir: config.providerStatusCacheDir, toolsDir: path.join(config.baseDir, "tools"), }); }), ), - AcpRegistryRuntimeCoordinator.layer, + AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator.layer, ); diff --git a/apps/server/src/provider/Layers/AntigravityProvider.test.ts b/apps/server/src/provider/Layers/AntigravityProvider.test.ts index 0b61104030ca..b2091f6db29f 100644 --- a/apps/server/src/provider/Layers/AntigravityProvider.test.ts +++ b/apps/server/src/provider/Layers/AntigravityProvider.test.ts @@ -18,7 +18,7 @@ import * as EffectAcpErrors from "effect-acp/errors"; import type * as EffectAcpSchema from "effect-acp/compat"; import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; -import { ServerSettingsService } from "../../serverSettings.ts"; +import * as ServerSettings from "../../serverSettings.ts"; import type { AcpSessionRuntimeStartResult } from "../acp/AcpSessionRuntime.ts"; import { buildAntigravityModelsFromSession, @@ -110,7 +110,7 @@ const testLayer = Layer.merge( Layer.mock(BackgroundPolicy.BackgroundPolicy)({ shouldRunScopeWork: () => Effect.succeed(false), }), - ServerSettingsService.layerTest(), + ServerSettings.layerTest(), ); type ProbeError = EffectAcpErrors.AcpError | ProviderSetupError; diff --git a/apps/server/src/provider/Layers/CursorProvider.test.ts b/apps/server/src/provider/Layers/CursorProvider.test.ts index 73b3d0af7101..213dddd8d7e2 100644 --- a/apps/server/src/provider/Layers/CursorProvider.test.ts +++ b/apps/server/src/provider/Layers/CursorProvider.test.ts @@ -13,7 +13,7 @@ import { buildInitialCursorProviderSnapshot, checkCursorProviderStatus, } from "./CursorProvider.ts"; -import { CursorSdkCatalogError, makeCursorSdkCatalogTestLayer } from "./CursorSdkCatalog.ts"; +import * as CursorSdkCatalog from "./CursorSdkCatalog.ts"; const decodeCursorSettings = Schema.decodeSync(CursorSettingsSchema); @@ -176,7 +176,7 @@ describe("checkCursorProviderStatus", () => { { CURSOR_API_KEY: "test-cursor-key" }, ).pipe( Effect.provide( - makeCursorSdkCatalogTestLayer((apiKey) => { + CursorSdkCatalog.makeCursorSdkCatalogTestLayer((apiKey) => { expect(apiKey).toBe("test-cursor-key"); return Effect.succeed({ user: { @@ -212,9 +212,9 @@ describe("checkCursorProviderStatus", () => { CURSOR_API_KEY: "invalid-test-key", }).pipe( Effect.provide( - makeCursorSdkCatalogTestLayer(() => + CursorSdkCatalog.makeCursorSdkCatalogTestLayer(() => Effect.fail( - new CursorSdkCatalogError({ + new CursorSdkCatalog.CursorSdkCatalogError({ authenticationFailure: true, cause: new Error("unauthorized"), }), @@ -235,7 +235,7 @@ describe("checkCursorProviderStatus", () => { Effect.gen(function* () { const provider = yield* checkCursorProviderStatus(baseCursorSettings).pipe( Effect.provide( - makeCursorSdkCatalogTestLayer(() => + CursorSdkCatalog.makeCursorSdkCatalogTestLayer(() => Effect.die("SDK catalog must not be used without CURSOR_API_KEY"), ), ), diff --git a/apps/server/src/provider/Layers/CursorProvider.ts b/apps/server/src/provider/Layers/CursorProvider.ts index f76778d6421d..95ee80be056a 100644 --- a/apps/server/src/provider/Layers/CursorProvider.ts +++ b/apps/server/src/provider/Layers/CursorProvider.ts @@ -21,7 +21,7 @@ import { providerModelsFromSettings, type ServerProviderDraft, } from "../providerSnapshot.ts"; -import { CursorSdkCatalog } from "./CursorSdkCatalog.ts"; +import * as CursorSdkCatalog from "./CursorSdkCatalog.ts"; const CURSOR_PRESENTATION = { displayName: "Cursor", @@ -250,7 +250,7 @@ export const checkCursorProviderStatus = Effect.fn("checkCursorProviderStatus")( cursorSettings: CursorSettings, environment?: NodeJS.ProcessEnv, authenticationType: "api-key" | "browser" = "api-key", -): Effect.fn.Return { +): Effect.fn.Return { const checkedAt = DateTime.formatIso(yield* DateTime.now); const fallbackModels = getCursorFallbackModels(cursorSettings); @@ -287,7 +287,7 @@ export const checkCursorProviderStatus = Effect.fn("checkCursorProviderStatus")( }); } - const sdkCatalog = yield* CursorSdkCatalog; + const sdkCatalog = yield* CursorSdkCatalog.CursorSdkCatalog; const catalogResult = yield* sdkCatalog .read(sdkApiKey) .pipe(Effect.timeoutOption(CURSOR_SDK_CATALOG_TIMEOUT_MS), Effect.result); diff --git a/apps/server/src/provider/Layers/CursorSdkCatalog.test.ts b/apps/server/src/provider/Layers/CursorSdkCatalog.test.ts index 8ab8eb6f707f..6cbd24f8b6be 100644 --- a/apps/server/src/provider/Layers/CursorSdkCatalog.test.ts +++ b/apps/server/src/provider/Layers/CursorSdkCatalog.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as Exit from "effect/Exit"; -import { CursorSdkCatalogError, makeCursorSdkCatalog } from "./CursorSdkCatalog.ts"; +import * as CursorSdkCatalog from "./CursorSdkCatalog.ts"; const user = { apiKeyName: "test-key", @@ -18,7 +18,7 @@ const model = { } satisfies SDKModel; const catalogError = () => - new CursorSdkCatalogError({ + new CursorSdkCatalog.CursorSdkCatalogError({ authenticationFailure: false, cause: new Error("catalog unavailable"), }); @@ -28,7 +28,7 @@ describe("CursorSdkCatalog", () => { Effect.gen(function* () { let userCalls = 0; let modelCalls = 0; - const catalog = yield* makeCursorSdkCatalog({ + const catalog = yield* CursorSdkCatalog.makeCursorSdkCatalog({ readUser: () => Effect.sync(() => ((userCalls += 1), user)), readModels: () => Effect.sync(() => ((modelCalls += 1), [model])), }); @@ -45,7 +45,7 @@ describe("CursorSdkCatalog", () => { it.effect("does not cache failed or empty model discovery", () => Effect.gen(function* () { let modelCalls = 0; - const catalog = yield* makeCursorSdkCatalog({ + const catalog = yield* CursorSdkCatalog.makeCursorSdkCatalog({ readUser: () => Effect.succeed(user), readModels: () => Effect.suspend(() => { diff --git a/apps/server/src/provider/Layers/ProviderAuthService.test.ts b/apps/server/src/provider/Layers/ProviderAuthService.test.ts index 2a29c46c5dee..be4acd6a6c38 100644 --- a/apps/server/src/provider/Layers/ProviderAuthService.test.ts +++ b/apps/server/src/provider/Layers/ProviderAuthService.test.ts @@ -20,18 +20,12 @@ import * as Queue from "effect/Queue"; import * as Stream from "effect/Stream"; import * as SubscriptionRef from "effect/SubscriptionRef"; -import { - ProjectionStoreReadError, - ProjectionStoreV2, -} from "../../orchestration-v2/ProjectionStore.ts"; -import { - ProviderSessionManagerV2, - ProviderSessionReleaseError, -} from "../../orchestration-v2/ProviderSessionManager.ts"; +import * as ProjectionStore from "../../orchestration-v2/ProjectionStore.ts"; +import * as ProviderSessionManager from "../../orchestration-v2/ProviderSessionManager.ts"; import { AcpProviderCapabilitiesV2 } from "../../orchestration-v2/Adapters/AcpAdapterV2.ts"; import type { ProviderInstance } from "../ProviderDriver.ts"; import type { ProviderAuthController } from "../Services/ProviderAuthService.ts"; -import { ProviderInstanceRegistry } from "../Services/ProviderInstanceRegistry.ts"; +import * as ProviderInstanceRegistry from "../Services/ProviderInstanceRegistry.ts"; import { makeProviderAuthService } from "./ProviderAuthService.ts"; const instanceId = ProviderInstanceId.make("antigravity-personal"); @@ -252,7 +246,7 @@ const makeHarness = Effect.fn("ProviderAuthService.test.makeHarness")(function* const service = yield* makeProviderAuthService.pipe( Effect.provide( Layer.mergeAll( - Layer.mock(ProviderInstanceRegistry)({ + Layer.mock(ProviderInstanceRegistry.ProviderInstanceRegistry)({ getInstance: (id) => Effect.gen(function* () { const found = instances.find((instance) => instance.instanceId === id); @@ -262,7 +256,7 @@ const makeHarness = Effect.fn("ProviderAuthService.test.makeHarness")(function* listInstances: Effect.succeed(instances), subscribeChanges: PubSub.subscribe(registryChanges), }), - Layer.mock(ProjectionStoreV2)({ + Layer.mock(ProjectionStore.ProjectionStoreV2)({ getRecoveryThreadIds: () => Effect.gen(function* () { assert.isTrue(gateClosed); @@ -271,7 +265,7 @@ const makeHarness = Effect.fn("ProviderAuthService.test.makeHarness")(function* yield* input.beforeListSessions ?? Effect.void; return yield* input.shellError ? Effect.fail( - new ProjectionStoreReadError({ + new ProjectionStore.ProjectionStoreReadError({ threadId: ThreadId.make("shell"), cause: new Error("private database diagnostics"), }), @@ -286,7 +280,7 @@ const makeHarness = Effect.fn("ProviderAuthService.test.makeHarness")(function* } as never; }), }), - Layer.mock(ProviderSessionManagerV2)({ + Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ release: ({ providerSessionId, reason }) => Effect.gen(function* () { assert.isTrue(gateClosed); @@ -294,7 +288,7 @@ const makeHarness = Effect.fn("ProviderAuthService.test.makeHarness")(function* yield* input.beforeStop ?? Effect.void; if (input.stopError) { return yield* Effect.fail( - new ProviderSessionReleaseError({ + new ProviderSessionManager.ProviderSessionReleaseError({ providerSessionId, reason, cause: new Error("private process diagnostics"), @@ -368,7 +362,7 @@ const makeSubscriptionHarness = Effect.fn("ProviderAuthService.test.makeSubscrip const service = yield* makeProviderAuthService.pipe( Effect.provide( Layer.mergeAll( - Layer.mock(ProviderInstanceRegistry)({ + Layer.mock(ProviderInstanceRegistry.ProviderInstanceRegistry)({ subscribeChanges: Effect.gen(function* () { const subscription = yield* PubSub.subscribe(changes); subscribed = true; @@ -386,8 +380,8 @@ const makeSubscriptionHarness = Effect.fn("ProviderAuthService.test.makeSubscrip return instance; }), }), - Layer.mock(ProjectionStoreV2)({}), - Layer.mock(ProviderSessionManagerV2)({}), + Layer.mock(ProjectionStore.ProjectionStoreV2)({}), + Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({}), ), ), ); diff --git a/apps/server/src/provider/Layers/ProviderAuthService.ts b/apps/server/src/provider/Layers/ProviderAuthService.ts index 8a9075dfc39c..cda9a5ed512b 100644 --- a/apps/server/src/provider/Layers/ProviderAuthService.ts +++ b/apps/server/src/provider/Layers/ProviderAuthService.ts @@ -8,15 +8,15 @@ import * as Layer from "effect/Layer"; import * as Stream from "effect/Stream"; import * as Semaphore from "effect/Semaphore"; -import { ProviderSessionManagerV2 } from "../../orchestration-v2/ProviderSessionManager.ts"; -import { ProjectionStoreV2 } from "../../orchestration-v2/ProjectionStore.ts"; +import * as ProviderSessionManager from "../../orchestration-v2/ProviderSessionManager.ts"; +import * as ProjectionStore from "../../orchestration-v2/ProjectionStore.ts"; import * as ProviderAuthService from "../Services/ProviderAuthService.ts"; -import { ProviderInstanceRegistry } from "../Services/ProviderInstanceRegistry.ts"; +import * as ProviderInstanceRegistry from "../Services/ProviderInstanceRegistry.ts"; export const makeProviderAuthService = Effect.gen(function* () { - const registry = yield* ProviderInstanceRegistry; - const projections = yield* ProjectionStoreV2; - const providerSessions = yield* ProviderSessionManagerV2; + const registry = yield* ProviderInstanceRegistry.ProviderInstanceRegistry; + const projections = yield* ProjectionStore.ProjectionStoreV2; + const providerSessions = yield* ProviderSessionManager.ProviderSessionManagerV2; const credentialChanges = yield* Semaphore.make(1); const getController = Effect.fn("ProviderAuthService.getController")(function* ( diff --git a/apps/server/src/provider/Layers/ProviderEventLoggers.ts b/apps/server/src/provider/Layers/ProviderEventLoggers.ts index 517a1c8195c0..654927440c56 100644 --- a/apps/server/src/provider/Layers/ProviderEventLoggers.ts +++ b/apps/server/src/provider/Layers/ProviderEventLoggers.ts @@ -28,7 +28,7 @@ import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import * as ResourceAttribution from "../../resourceTelemetry/ResourceAttribution.ts"; import * as EventNdjsonLogger from "./EventNdjsonLogger.ts"; @@ -65,7 +65,7 @@ export const NoOpProviderEventLoggers: ProviderEventLoggers["Service"] = { * @public Service construction is part of the canonical Effect module API. */ export const make = Effect.gen(function* () { - const { providerEventLogPath } = yield* ServerConfig; + const { providerEventLogPath } = yield* ServerConfig.ServerConfig; const attribution = yield* ResourceAttribution.ResourceAttribution; const store = yield* EventNdjsonLogger.makeEventNdjsonLogStore(providerEventLogPath, { attribution, diff --git a/apps/server/src/provider/Layers/ProviderInstanceRegistryHydration.ts b/apps/server/src/provider/Layers/ProviderInstanceRegistryHydration.ts index 5ce2a9ea0bd5..d0bfb8d73986 100644 --- a/apps/server/src/provider/Layers/ProviderInstanceRegistryHydration.ts +++ b/apps/server/src/provider/Layers/ProviderInstanceRegistryHydration.ts @@ -51,21 +51,24 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Stream from "effect/Stream"; -import { ServerSettingsService } from "../../serverSettings.ts"; +import * as Settings from "../../serverSettings.ts"; import { BUILT_IN_DRIVERS, type BuiltInDriversEnv } from "../builtInDrivers.ts"; -import { ProviderInstanceRegistry } from "../Services/ProviderInstanceRegistry.ts"; -import { ProviderInstanceRegistryMutator } from "../Services/ProviderInstanceRegistryMutator.ts"; +import * as ProviderInstanceRegistry from "../Services/ProviderInstanceRegistry.ts"; +import * as ProviderInstanceRegistryMutator from "../Services/ProviderInstanceRegistryMutator.ts"; import { ProviderInstanceRegistryMutableLayer } from "./ProviderInstanceRegistryLive.ts"; import { type ProviderOrchestrationAdapterInfrastructure, ProviderOrchestrationAdapterInfrastructureLive, } from "./ProviderOrchestrationAdapterInfrastructure.ts"; -import { AcpRegistryCatalog } from "../acp/AcpRegistrySupport.ts"; +import * as AcpRegistrySupport from "../acp/AcpRegistrySupport.ts"; import { AcpRegistryCatalogLive } from "./AcpRegistryCatalog.ts"; type ProviderInstanceRegistryHydrationEnv = - | Exclude - | ServerSettingsService; + | Exclude< + BuiltInDriversEnv, + ProviderOrchestrationAdapterInfrastructure | AcpRegistrySupport.AcpRegistryCatalog + > + | Settings.ServerSettingsService; /** * Synthesize a `ProviderInstanceConfigMap` from a `ServerSettings` snapshot. @@ -126,8 +129,8 @@ export const deriveProviderInstanceConfigMap = ( */ const SettingsWatcherLive = Layer.effectDiscard( Effect.gen(function* () { - const mutator = yield* ProviderInstanceRegistryMutator; - const serverSettings = yield* ServerSettingsService; + const mutator = yield* ProviderInstanceRegistryMutator.ProviderInstanceRegistryMutator; + const serverSettings = yield* Settings.ServerSettingsService; const settingsChanges = yield* serverSettings.subscribeChanges; yield* settingsChanges.pipe( Stream.runForEach((next) => @@ -161,12 +164,12 @@ const SettingsWatcherLive = Layer.effectDiscard( * it, so the visibility leak is harmless in practice. */ export const ProviderInstanceRegistryHydrationLive: Layer.Layer< - ProviderInstanceRegistry, + ProviderInstanceRegistry.ProviderInstanceRegistry, never, ProviderInstanceRegistryHydrationEnv > = Layer.unwrap( Effect.gen(function* () { - const serverSettings = yield* ServerSettingsService; + const serverSettings = yield* Settings.ServerSettingsService; const initialSettings: ServerSettings | undefined = yield* serverSettings.getSettings.pipe( Effect.orElseSucceed(() => undefined), ); @@ -185,4 +188,8 @@ export const ProviderInstanceRegistryHydrationLive: Layer.Layer< return SettingsWatcherLive.pipe(Layer.provideMerge(mutableLayer)); }), -) as Layer.Layer; +) as Layer.Layer< + ProviderInstanceRegistry.ProviderInstanceRegistry, + never, + ProviderInstanceRegistryHydrationEnv +>; diff --git a/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts b/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts index 505c381641b9..4d383a7dd16b 100644 --- a/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts +++ b/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts @@ -1,6 +1,6 @@ import * as ServerSecretStore from "../../auth/ServerSecretStore.ts"; -import { CodexInstallation } from "../CodexInstallation.ts"; -import { ServerEnvironmentIdentity } from "../../environment/ServerEnvironment.ts"; +import * as CodexInstallation from "../CodexInstallation.ts"; +import * as ServerEnvironment from "../../environment/ServerEnvironment.ts"; /** * Multi-instance validation slices for `ProviderInstanceRegistryLive`. * @@ -48,20 +48,20 @@ import * as Stream from "effect/Stream"; import { HttpClient, HttpClientResponse } from "effect/unstable/http"; import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; -import { AntigravityInstallation } from "../AntigravityInstallation.ts"; -import { ServerConfig } from "../../config.ts"; +import * as AntigravityInstallation from "../AntigravityInstallation.ts"; +import * as ServerConfig from "../../config.ts"; import { expandHomePath } from "../../pathExpansion.ts"; -import { ServerSettingsService } from "../../serverSettings.ts"; +import * as ServerSettings from "../../serverSettings.ts"; import { ClaudeDriver, type ClaudeDriverEnv } from "../Drivers/ClaudeDriver.ts"; import { CodexDriver, type CodexDriverEnv } from "../Drivers/CodexDriver.ts"; import { CursorDriver, type CursorDriverEnv } from "../Drivers/CursorDriver.ts"; import { GrokDriver, type GrokDriverEnv } from "../Drivers/GrokDriver.ts"; import { OpenCodeDriver, type OpenCodeDriverEnv } from "../Drivers/OpenCodeDriver.ts"; import * as ModelManifest from "../ModelManifest.ts"; -import { OpenCodeRuntimeLive } from "../opencodeRuntime.ts"; +import * as OpenCodeRuntime from "../opencodeRuntime.ts"; import * as OpenCodeServerLedger from "../OpenCodeServerLedger.ts"; import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts"; -import { NoOpProviderEventLoggers, ProviderEventLoggers } from "./ProviderEventLoggers.ts"; +import * as ProviderEventLoggers from "./ProviderEventLoggers.ts"; import { makeProviderInstanceRegistry } from "./ProviderInstanceRegistryLive.ts"; import { ProviderOrchestrationAdapterInfrastructureLive } from "./ProviderOrchestrationAdapterInfrastructure.ts"; @@ -250,21 +250,28 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { }).pipe( Layer.provideMerge(NodeServices.layer), Layer.provideMerge( - Layer.mock(CodexInstallation)({ managedDirectory: "unused-managed-installation" }), + Layer.mock(CodexInstallation.CodexInstallation)({ + managedDirectory: "unused-managed-installation", + }), ), Layer.provideMerge(Layer.mock(ServerSecretStore.ServerSecretStore)({})), Layer.provideMerge( - Layer.succeed(ServerEnvironmentIdentity, { + Layer.succeed(ServerEnvironment.ServerEnvironmentIdentity, { getEnvironmentId: Effect.succeed( EnvironmentId.make("00000000-0000-4000-8000-000000000001"), ), }), ), Layer.provideMerge(BackgroundPolicyAlwaysRunLayer), - Layer.provideMerge(ServerSettingsService.layerTest()), + Layer.provideMerge(ServerSettings.layerTest()), Layer.provideMerge(TestHttpClientLive), - Layer.provideMerge(ServerSettingsService.layerTest()), - Layer.provideMerge(Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers)), + Layer.provideMerge(ServerSettings.layerTest()), + Layer.provideMerge( + Layer.succeed( + ProviderEventLoggers.ProviderEventLoggers, + ProviderEventLoggers.NoOpProviderEventLoggers, + ), + ), Layer.provideMerge(ModelManifest.layerTest), Layer.provideMerge(ResetCreditCoordinator.layerTest), ); @@ -612,21 +619,23 @@ describe("ProviderInstanceRegistryLive — all drivers slice", () => { // provides `OpenCodeRuntimeLive`'s deps while keeping its own outputs // surfaced; that merged layer then provides `ServerConfig.layerTest`'s // `FileSystem` dep while keeping everything else surfaced to the test. - const infraLayer = OpenCodeRuntimeLive.pipe( + const infraLayer = OpenCodeRuntime.OpenCodeRuntimeLive.pipe( Layer.provide(OpenCodeServerLedger.layerTest), Layer.provideMerge(NodeServices.layer), Layer.provideMerge( - Layer.mock(CodexInstallation)({ managedDirectory: "unused-managed-installation" }), + Layer.mock(CodexInstallation.CodexInstallation)({ + managedDirectory: "unused-managed-installation", + }), ), Layer.provideMerge( - Layer.succeed(ServerEnvironmentIdentity, { + Layer.succeed(ServerEnvironment.ServerEnvironmentIdentity, { getEnvironmentId: Effect.succeed( EnvironmentId.make("00000000-0000-4000-8000-000000000001"), ), }), ), ); - const baseLayer = AntigravityInstallation.layer.pipe( + const baseLayer = AntigravityInstallation.AntigravityInstallation.layer.pipe( Layer.provideMerge(ServerSecretStore.layer), Layer.provideMerge( ServerConfig.layerTest(process.cwd(), { @@ -635,10 +644,15 @@ describe("ProviderInstanceRegistryLive — all drivers slice", () => { ), Layer.provideMerge(infraLayer), Layer.provideMerge(BackgroundPolicyAlwaysRunLayer), - Layer.provideMerge(ServerSettingsService.layerTest()), + Layer.provideMerge(ServerSettings.layerTest()), Layer.provideMerge(TestHttpClientLive), - Layer.provideMerge(ServerSettingsService.layerTest()), - Layer.provideMerge(Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers)), + Layer.provideMerge(ServerSettings.layerTest()), + Layer.provideMerge( + Layer.succeed( + ProviderEventLoggers.ProviderEventLoggers, + ProviderEventLoggers.NoOpProviderEventLoggers, + ), + ), Layer.provideMerge(ModelManifest.layerTest), Layer.provideMerge(ResetCreditCoordinator.layerTest), ); diff --git a/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.ts b/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.ts index 67ccbd167c30..4d59d2f57f98 100644 --- a/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.ts +++ b/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.ts @@ -52,14 +52,8 @@ import * as Scope from "effect/Scope"; import * as Stream from "effect/Stream"; import { buildUnavailableProviderSnapshot } from "../unavailableProviderSnapshot.ts"; -import { - ProviderInstanceRegistry, - type ProviderInstanceRegistryShape, -} from "../Services/ProviderInstanceRegistry.ts"; -import { - ProviderInstanceRegistryMutator, - type ProviderInstanceRegistryMutatorShape, -} from "../Services/ProviderInstanceRegistryMutator.ts"; +import * as ProviderInstanceRegistry from "../Services/ProviderInstanceRegistry.ts"; +import * as ProviderInstanceRegistryMutator from "../Services/ProviderInstanceRegistryMutator.ts"; import type { AnyProviderDriver, ProviderInstance } from "../ProviderDriver.ts"; /** @@ -340,8 +334,8 @@ export const makeProviderInstanceRegistry = (input: { readonly configMap: ProviderInstanceConfigMap; }): Effect.Effect< { - readonly registry: ProviderInstanceRegistryShape; - readonly mutator: ProviderInstanceRegistryMutatorShape; + readonly registry: ProviderInstanceRegistry.ProviderInstanceRegistryShape; + readonly mutator: ProviderInstanceRegistryMutator.ProviderInstanceRegistryMutatorShape; }, never, R | Scope.Scope @@ -370,14 +364,14 @@ export const makeProviderInstanceRegistry = (input: { const state: RegistryState = { entries, unavailable, changes }; const reconcileWithR = makeReconcile({ state, driversById, parentScope }); - const reconcile: ProviderInstanceRegistryMutatorShape["reconcile"] = (configMap) => - reconcileWithR(configMap).pipe(Effect.provideContext(driverContext)); + const reconcile: ProviderInstanceRegistryMutator.ProviderInstanceRegistryMutatorShape["reconcile"] = + (configMap) => reconcileWithR(configMap).pipe(Effect.provideContext(driverContext)); // Hydrate the initial configMap synchronously so callers can read // `listInstances` immediately after this effect completes. yield* reconcile(input.configMap); - const registry: ProviderInstanceRegistryShape = { + const registry: ProviderInstanceRegistry.ProviderInstanceRegistryShape = { getInstance: (id) => Ref.get(entries).pipe(Effect.map((map) => map.get(id)?.instance)), listInstances: Ref.get(entries).pipe( Effect.map( @@ -405,7 +399,9 @@ export const makeProviderInstanceRegistry = (input: { }, }; - const mutator: ProviderInstanceRegistryMutatorShape = { reconcile }; + const mutator: ProviderInstanceRegistryMutator.ProviderInstanceRegistryMutatorShape = { + reconcile, + }; return { registry, mutator }; }); @@ -419,13 +415,23 @@ export const makeProviderInstanceRegistry = (input: { export const ProviderInstanceRegistryMutableLayer = (input: { readonly drivers: ReadonlyArray>; readonly configMap: ProviderInstanceConfigMap; -}): Layer.Layer => +}): Layer.Layer< + | ProviderInstanceRegistry.ProviderInstanceRegistry + | ProviderInstanceRegistryMutator.ProviderInstanceRegistryMutator, + never, + R +> => Layer.effectContext( makeProviderInstanceRegistry(input).pipe( Effect.map(({ registry, mutator }) => - Context.make(ProviderInstanceRegistry, registry).pipe( - Context.add(ProviderInstanceRegistryMutator, mutator), + Context.make(ProviderInstanceRegistry.ProviderInstanceRegistry, registry).pipe( + Context.add(ProviderInstanceRegistryMutator.ProviderInstanceRegistryMutator, mutator), ), ), ), - ) as Layer.Layer; + ) as Layer.Layer< + | ProviderInstanceRegistry.ProviderInstanceRegistry + | ProviderInstanceRegistryMutator.ProviderInstanceRegistryMutator, + never, + R + >; diff --git a/apps/server/src/provider/Layers/ProviderOrchestrationAdapterInfrastructure.ts b/apps/server/src/provider/Layers/ProviderOrchestrationAdapterInfrastructure.ts index 747654fe39f6..c9d8d5e70570 100644 --- a/apps/server/src/provider/Layers/ProviderOrchestrationAdapterInfrastructure.ts +++ b/apps/server/src/provider/Layers/ProviderOrchestrationAdapterInfrastructure.ts @@ -1,25 +1,16 @@ import * as Layer from "effect/Layer"; -import { - ClaudeAgentSdkQueryRunner, - claudeAgentSdkQueryRunnerLiveLayer, -} from "../../orchestration-v2/Adapters/ClaudeAdapterV2.ts"; -import { - CodexAppServerClientFactory, - codexAppServerClientFactoryFromSettingsLayer, -} from "../../orchestration-v2/Adapters/CodexAdapterV2.ts"; -import { - CursorAgentSdkRunner, - cursorAgentSdkRunnerLiveLayer, -} from "../../orchestration-v2/Adapters/CursorAgentSdk.ts"; -import { IdAllocatorV2, layer as idAllocatorLayer } from "../../orchestration-v2/IdAllocator.ts"; -import { layer as providerContinuationRequestsLayer } from "../../orchestration-v2/ProviderContinuationRequests.ts"; +import * as ClaudeAdapterV2 from "../../orchestration-v2/Adapters/ClaudeAdapterV2.ts"; +import * as CodexAdapterV2 from "../../orchestration-v2/Adapters/CodexAdapterV2.ts"; +import * as CursorAgentSdk from "../../orchestration-v2/Adapters/CursorAgentSdk.ts"; +import * as IdAllocator from "../../orchestration-v2/IdAllocator.ts"; +import * as ProviderContinuationRequests from "../../orchestration-v2/ProviderContinuationRequests.ts"; export type ProviderOrchestrationAdapterInfrastructure = - | ClaudeAgentSdkQueryRunner - | CodexAppServerClientFactory - | CursorAgentSdkRunner - | IdAllocatorV2; + | ClaudeAdapterV2.ClaudeAgentSdkQueryRunner + | CodexAdapterV2.CodexAppServerClientFactory + | CursorAgentSdk.CursorAgentSdkRunner + | IdAllocator.IdAllocatorV2; /** * Infrastructure shared by the V2 adapters materialized inside provider @@ -28,9 +19,9 @@ export type ProviderOrchestrationAdapterInfrastructure = * Effect layer memoization yields one shared queue. */ export const ProviderOrchestrationAdapterInfrastructureLive = Layer.mergeAll( - claudeAgentSdkQueryRunnerLiveLayer, - codexAppServerClientFactoryFromSettingsLayer, - cursorAgentSdkRunnerLiveLayer, - idAllocatorLayer, - providerContinuationRequestsLayer, + ClaudeAdapterV2.claudeAgentSdkQueryRunnerLiveLayer, + CodexAdapterV2.codexAppServerClientFactoryFromSettingsLayer, + CursorAgentSdk.cursorAgentSdkRunnerLiveLayer, + IdAllocator.layer, + ProviderContinuationRequests.layer, ); diff --git a/apps/server/src/provider/Layers/ProviderRegistry.test.ts b/apps/server/src/provider/Layers/ProviderRegistry.test.ts index 1e49392f20bd..30da9e8befc2 100644 --- a/apps/server/src/provider/Layers/ProviderRegistry.test.ts +++ b/apps/server/src/provider/Layers/ProviderRegistry.test.ts @@ -1,6 +1,6 @@ import * as ServerSecretStore from "../../auth/ServerSecretStore.ts"; -import { CodexInstallation } from "../CodexInstallation.ts"; -import { ServerEnvironmentIdentity } from "../../environment/ServerEnvironment.ts"; +import * as CodexInstallation from "../CodexInstallation.ts"; +import * as ServerEnvironment from "../../environment/ServerEnvironment.ts"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { describe, it, assert } from "@effect/vitest"; import * as DateTime from "effect/DateTime"; @@ -40,7 +40,7 @@ import { applyServerSettingsPatch } from "@t3tools/shared/serverSettings"; import { checkCodexProviderStatus, type CodexAppServerProviderSnapshot } from "./CodexProvider.ts"; import { checkClaudeProviderStatus } from "./ClaudeProvider.ts"; import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; -import { AntigravityInstallation } from "../AntigravityInstallation.ts"; +import * as AntigravityInstallation from "../AntigravityInstallation.ts"; import * as ModelManifest from "../ModelManifest.ts"; import { applyProviderCompatibility } from "../providerCompatibility.ts"; import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts"; @@ -391,9 +391,11 @@ const awaitPersistedProvider = ( const TestNodeServices = Layer.mergeAll( NodeServices.layer, - Layer.mock(CodexInstallation)({ managedDirectory: "unused-managed-installation" }), + Layer.mock(CodexInstallation.CodexInstallation)({ + managedDirectory: "unused-managed-installation", + }), Layer.mock(ServerSecretStore.ServerSecretStore)({}), - Layer.succeed(ServerEnvironmentIdentity, { + Layer.succeed(ServerEnvironment.ServerEnvironmentIdentity, { getEnvironmentId: Effect.succeed(EnvironmentId.make("00000000-0000-4000-8000-000000000001")), }), ); @@ -2473,7 +2475,7 @@ it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), Test yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); const providerRegistryLayer = ProviderRegistryLive.pipe( Layer.provideMerge(ProviderInstanceRegistryHydrationLive), - Layer.provideMerge(AntigravityInstallation.layer), + Layer.provideMerge(AntigravityInstallation.AntigravityInstallation.layer), Layer.provideMerge( Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), ), @@ -2577,7 +2579,7 @@ it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), Test yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); const providerRegistryLayer = ProviderRegistryLive.pipe( Layer.provideMerge(ProviderInstanceRegistryHydrationLive), - Layer.provideMerge(AntigravityInstallation.layer), + Layer.provideMerge(AntigravityInstallation.AntigravityInstallation.layer), Layer.provideMerge( Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), ), @@ -2698,7 +2700,7 @@ it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), Test yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); const providerRegistryLayer = ProviderRegistryLive.pipe( Layer.provideMerge(ProviderInstanceRegistryHydrationLive), - Layer.provideMerge(AntigravityInstallation.layer), + Layer.provideMerge(AntigravityInstallation.AntigravityInstallation.layer), Layer.provideMerge( Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), ), @@ -2765,7 +2767,7 @@ it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), Test yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); const providerRegistryLayer = ProviderRegistryLive.pipe( Layer.provideMerge(ProviderInstanceRegistryHydrationLive), - Layer.provideMerge(AntigravityInstallation.layer), + Layer.provideMerge(AntigravityInstallation.AntigravityInstallation.layer), Layer.provideMerge( Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), ), diff --git a/apps/server/src/provider/Layers/ProviderRegistry.ts b/apps/server/src/provider/Layers/ProviderRegistry.ts index 4316a2dc7fe2..b0d61b9a1f6e 100644 --- a/apps/server/src/provider/Layers/ProviderRegistry.ts +++ b/apps/server/src/provider/Layers/ProviderRegistry.ts @@ -42,9 +42,9 @@ import * as Semaphore from "effect/Semaphore"; import * as ModelManifest from "../ModelManifest.ts"; import { applyProviderCompatibility } from "../providerCompatibility.ts"; -import { ServerConfig } from "../../config.ts"; -import { ProviderInstanceRegistry } from "../Services/ProviderInstanceRegistry.ts"; -import { ProviderRegistry, type ProviderRegistryShape } from "../Services/ProviderRegistry.ts"; +import * as ServerConfig from "../../config.ts"; +import * as ProviderInstanceRegistry from "../Services/ProviderInstanceRegistry.ts"; +import * as ProviderRegistry from "../Services/ProviderRegistry.ts"; import { hydrateCachedProvider, isCachedProviderCorrelated, @@ -314,12 +314,12 @@ const buildSnapshotSource = (instance: ProviderInstance): ProviderSnapshotSource }); export const ProviderRegistryLive = Layer.effect( - ProviderRegistry, + ProviderRegistry.ProviderRegistry, Effect.gen(function* () { - const instanceRegistry = yield* ProviderInstanceRegistry; + const instanceRegistry = yield* ProviderInstanceRegistry.ProviderInstanceRegistry; const manifestService = yield* ModelManifest.ModelManifest; const serviceScope = yield* Effect.scope; - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; @@ -947,6 +947,6 @@ export const ProviderRegistryLive = Layer.effect( get streamChanges() { return Stream.fromPubSub(changesPubSub); }, - } satisfies ProviderRegistryShape; + } satisfies ProviderRegistry.ProviderRegistryShape; }), ); diff --git a/apps/server/src/provider/ModelManifest.test.ts b/apps/server/src/provider/ModelManifest.test.ts index 7933f8e2e962..5bfd13a6947d 100644 --- a/apps/server/src/provider/ModelManifest.test.ts +++ b/apps/server/src/provider/ModelManifest.test.ts @@ -10,15 +10,7 @@ import { HttpClient, HttpClientResponse } from "effect/unstable/http"; import * as ServerConfig from "../config.ts"; import * as ServerSettings from "../serverSettings.ts"; -import { - applyManifestDefault, - BUNDLED_MODEL_MANIFEST, - classifyModels, - make, - resolveProviderCatalog, - type ModelManifestData, - encodeManifestCache, -} from "./ModelManifest.ts"; +import * as ModelManifest from "./ModelManifest.ts"; /** * Test policy: this file covers manifest machinery, not manifest contents. @@ -39,13 +31,19 @@ const model = (overrides: Partial): ServerProviderModel => describe("classifyModels", () => { it("classifies qualified Codex families without changing their wire ids", () => { - const manifest: ModelManifestData = { version: 1, currentModels: { codex: ["gpt-test"] } }; + const manifest: ModelManifest.ModelManifestData = { + version: 1, + currentModels: { codex: ["gpt-test"] }, + }; const models = [ model({ slug: "openai.gpt-test", isLegacy: true }), model({ slug: "openai.gpt-old" }), ]; assert.deepStrictEqual( - classifyModels(models, manifest, CODEX).map((entry) => [entry.slug, entry.isLegacy ?? false]), + ModelManifest.classifyModels(models, manifest, CODEX).map((entry) => [ + entry.slug, + entry.isLegacy ?? false, + ]), [ ["openai.gpt-test", false], ["openai.gpt-old", true], @@ -53,7 +51,7 @@ describe("classifyModels", () => { ); }); it("flags non-current models, clears stale flags, and skips custom models", () => { - const manifest: ModelManifestData = { + const manifest: ModelManifest.ModelManifestData = { version: 1, currentModels: { codex: ["current-a", "current-b"] }, }; @@ -66,7 +64,10 @@ describe("classifyModels", () => { model({ slug: "my-own-model", isCustom: true }), ]; assert.deepStrictEqual( - classifyModels(models, manifest, CODEX).map((entry) => [entry.slug, entry.isLegacy ?? false]), + ModelManifest.classifyModels(models, manifest, CODEX).map((entry) => [ + entry.slug, + entry.isLegacy ?? false, + ]), [ ["current-a", false], ["current-b", false], @@ -79,7 +80,7 @@ describe("classifyModels", () => { describe("applyManifestDefault", () => { it("resolves the manifest default to the qualified live model", () => { - const manifest: ModelManifestData = { + const manifest: ModelManifest.ModelManifestData = { version: 1, currentModels: {}, providers: { codex: { models: [], profiles: {}, defaults: { chat: "gpt-test" } } }, @@ -89,13 +90,14 @@ describe("applyManifestDefault", () => { model({ slug: "openai.gpt-test" }), ]; assert.strictEqual( - applyManifestDefault(models, manifest, CODEX).find((entry) => entry.isDefault)?.slug, + ModelManifest.applyManifestDefault(models, manifest, CODEX).find((entry) => entry.isDefault) + ?.slug, "openai.gpt-test", ); }); it("moves the default flag and its aliases to the manifest's chat default", () => { const driver = ProviderDriverKind.make("antigravity"); - const manifest: ModelManifestData = { + const manifest: ModelManifest.ModelManifestData = { version: 1, currentModels: {}, providers: { @@ -110,13 +112,13 @@ describe("applyManifestDefault", () => { model({ slug: "gemini-old", isDefault: true, aliases: ["antigravity-default"] }), model({ slug: "gemini-new" }), ]; - assert.deepStrictEqual(applyManifestDefault(models, manifest, driver), [ + assert.deepStrictEqual(ModelManifest.applyManifestDefault(models, manifest, driver), [ model({ slug: "gemini-old" }), model({ slug: "gemini-new", isDefault: true, aliases: ["antigravity-default"] }), ]); // The account does not offer the manifest default: keep the runtime's choice. assert.deepStrictEqual( - applyManifestDefault(models.slice(0, 1), manifest, driver), + ModelManifest.applyManifestDefault(models.slice(0, 1), manifest, driver), models.slice(0, 1), ); }); @@ -124,7 +126,7 @@ describe("applyManifestDefault", () => { describe("resolveProviderCatalog", () => { it("resolves generic model presentation through a reusable profile", () => { - const manifest: ModelManifestData = { + const manifest: ModelManifest.ModelManifestData = { version: 1, currentModels: {}, providers: { @@ -159,7 +161,10 @@ describe("resolveProviderCatalog", () => { }, }; - const catalog = resolveProviderCatalog(manifest, ProviderDriverKind.make("synthetic")); + const catalog = ModelManifest.resolveProviderCatalog( + manifest, + ProviderDriverKind.make("synthetic"), + ); assert.deepStrictEqual(catalog?.models[0], { model: { slug: "model-next", @@ -177,9 +182,9 @@ describe("resolveProviderCatalog", () => { it("rejects invalid catalog references", () => { const invalidCatalog = (input: { - readonly models: NonNullable[string]["models"]; + readonly models: NonNullable[string]["models"]; readonly defaultChat?: string; - }): ModelManifestData => ({ + }): ModelManifest.ModelManifestData => ({ version: 1, currentModels: {}, providers: { @@ -213,7 +218,9 @@ describe("resolveProviderCatalog", () => { defaultChat: "absent", }), ]) { - assert.isNull(resolveProviderCatalog(invalid, ProviderDriverKind.make("synthetic"))); + assert.isNull( + ModelManifest.resolveProviderCatalog(invalid, ProviderDriverKind.make("synthetic")), + ); } }); }); @@ -221,7 +228,7 @@ describe("resolveProviderCatalog", () => { // Remote fixtures date after the bundle so a fetch still outranks it. const REMOTE_UPDATED_AT = "2099-01-01T00:00:00Z"; -const REMOTE_MANIFEST: ModelManifestData = { +const REMOTE_MANIFEST: ModelManifest.ModelManifestData = { version: 1, updatedAt: REMOTE_UPDATED_AT, currentModels: { @@ -230,7 +237,7 @@ const REMOTE_MANIFEST: ModelManifestData = { }, }; -const REMOTE_CLAUDE_MANIFEST: ModelManifestData = { +const REMOTE_CLAUDE_MANIFEST: ModelManifest.ModelManifestData = { version: 1, updatedAt: REMOTE_UPDATED_AT, currentModels: {}, @@ -253,7 +260,9 @@ const REMOTE_CLAUDE_MANIFEST: ModelManifestData = { }, }; -const remoteClaudeManifestWithCompatibility = (compatibility: unknown): ModelManifestData => ({ +const remoteClaudeManifestWithCompatibility = ( + compatibility: unknown, +): ModelManifest.ModelManifestData => ({ ...REMOTE_CLAUDE_MANIFEST, providers: { claudeAgent: { @@ -266,7 +275,7 @@ const remoteClaudeManifestWithCompatibility = (compatibility: unknown): ModelMan }, }); -const INVALID_REMOTE_MANIFESTS: ReadonlyArray = [ +const INVALID_REMOTE_MANIFESTS: ReadonlyArray = [ { ...REMOTE_CLAUDE_MANIFEST, providers: { @@ -344,23 +353,23 @@ const serviceLayers = (input: { describe("ModelManifest service", () => { it.live("explicit refresh bypasses fresh memory and disk caches", () => { let fetchCount = 0; - const updated: ModelManifestData = { + const updated: ModelManifest.ModelManifestData = { ...REMOTE_MANIFEST, currentModels: { codex: ["gpt-reloaded"] }, }; return Effect.gen(function* () { - const service = yield* make; + const service = yield* ModelManifest.make; assert.deepStrictEqual(yield* service.refresh, REMOTE_MANIFEST); assert.deepStrictEqual(yield* service.refresh, REMOTE_MANIFEST); assert.strictEqual(fetchCount, 1); - const rebooted = yield* make; + const rebooted = yield* ModelManifest.make; assert.deepStrictEqual(yield* rebooted.refresh, REMOTE_MANIFEST); assert.strictEqual(fetchCount, 1); assert.deepStrictEqual(yield* rebooted.forceRefresh, updated); assert.strictEqual(fetchCount, 2); assert.deepStrictEqual(yield* rebooted.current, updated); - assert.deepStrictEqual(yield* (yield* make).current, updated); + assert.deepStrictEqual(yield* (yield* ModelManifest.make).current, updated); }).pipe( Effect.scoped, Effect.provide( @@ -375,11 +384,11 @@ describe("ModelManifest service", () => { it.live("explicit refresh retries immediately after failure and preserves last-good data", () => { let fetchCount = 0; return Effect.gen(function* () { - const service = yield* make; + const service = yield* ModelManifest.make; assert.deepStrictEqual(yield* service.refresh, REMOTE_MANIFEST); assert.deepStrictEqual(yield* service.forceRefresh, REMOTE_MANIFEST); assert.deepStrictEqual(yield* service.current, REMOTE_MANIFEST); - assert.deepStrictEqual(yield* (yield* make).current, REMOTE_MANIFEST); + assert.deepStrictEqual(yield* (yield* ModelManifest.make).current, REMOTE_MANIFEST); assert.strictEqual(fetchCount, 2); assert.deepStrictEqual(yield* service.forceRefresh, REMOTE_MANIFEST); assert.strictEqual(fetchCount, 3); @@ -400,9 +409,9 @@ describe("ModelManifest service", () => { it.live("explicit refresh bypasses the retry delay after an initial failure", () => { let fetchCount = 0; return Effect.gen(function* () { - const service = yield* make; - assert.deepStrictEqual(yield* service.refresh, BUNDLED_MODEL_MANIFEST); - assert.deepStrictEqual(yield* service.refresh, BUNDLED_MODEL_MANIFEST); + const service = yield* ModelManifest.make; + assert.deepStrictEqual(yield* service.refresh, ModelManifest.BUNDLED_MODEL_MANIFEST); + assert.deepStrictEqual(yield* service.refresh, ModelManifest.BUNDLED_MODEL_MANIFEST); assert.strictEqual(fetchCount, 1); assert.deepStrictEqual(yield* service.forceRefresh, REMOTE_MANIFEST); assert.strictEqual(fetchCount, 2); @@ -422,13 +431,13 @@ describe("ModelManifest service", () => { it.live("prefers a fetched manifest over the bundle and caches it to disk", () => Effect.gen(function* () { - const service = yield* make; + const service = yield* ModelManifest.make; const refreshed = yield* service.refresh; assert.deepStrictEqual(refreshed, REMOTE_MANIFEST); // A fresh service instance sees the disk cache without another fetch: // its HTTP layer is still stubbed, but `current` never fetches at all. - const rebooted = yield* make; + const rebooted = yield* ModelManifest.make; assert.deepStrictEqual(yield* rebooted.current, REMOTE_MANIFEST); }).pipe( Effect.scoped, @@ -444,15 +453,15 @@ describe("ModelManifest service", () => { it.live("ignores older remote edits without replacing the current manifest or disk cache", () => { let remote = { ...REMOTE_MANIFEST, updatedAt: "2000-01-01T00:00:00Z" }; return Effect.gen(function* () { - const service = yield* make; - assert.deepStrictEqual(yield* service.refresh, BUNDLED_MODEL_MANIFEST); - assert.deepStrictEqual(yield* service.current, BUNDLED_MODEL_MANIFEST); + const service = yield* ModelManifest.make; + assert.deepStrictEqual(yield* service.refresh, ModelManifest.BUNDLED_MODEL_MANIFEST); + assert.deepStrictEqual(yield* service.current, ModelManifest.BUNDLED_MODEL_MANIFEST); remote = { ...REMOTE_MANIFEST, updatedAt: REMOTE_UPDATED_AT }; assert.deepStrictEqual(yield* service.forceRefresh, REMOTE_MANIFEST); - remote = { ...REMOTE_MANIFEST, updatedAt: BUNDLED_MODEL_MANIFEST.updatedAt! }; + remote = { ...REMOTE_MANIFEST, updatedAt: ModelManifest.BUNDLED_MODEL_MANIFEST.updatedAt! }; assert.deepStrictEqual(yield* service.forceRefresh, REMOTE_MANIFEST); - const rebooted = yield* make; + const rebooted = yield* ModelManifest.make; assert.deepStrictEqual(yield* rebooted.current, REMOTE_MANIFEST); }).pipe( Effect.scoped, @@ -467,8 +476,8 @@ describe("ModelManifest service", () => { it.live("keeps the bundled manifest when the remote payload is malformed", () => Effect.gen(function* () { - const service = yield* make; - assert.deepStrictEqual(yield* service.refresh, BUNDLED_MODEL_MANIFEST); + const service = yield* ModelManifest.make; + assert.deepStrictEqual(yield* service.refresh, ModelManifest.BUNDLED_MODEL_MANIFEST); }).pipe( Effect.scoped, Effect.provide( @@ -485,7 +494,7 @@ describe("ModelManifest service", () => { const responses = [REMOTE_CLAUDE_MANIFEST, ...INVALID_REMOTE_MANIFESTS]; return Effect.gen(function* () { - const service = yield* make; + const service = yield* ModelManifest.make; assert.deepStrictEqual(yield* service.refresh, REMOTE_CLAUDE_MANIFEST); for (const _invalid of INVALID_REMOTE_MANIFESTS) { @@ -494,7 +503,7 @@ describe("ModelManifest service", () => { assert.deepStrictEqual(yield* service.refresh, REMOTE_CLAUDE_MANIFEST); } - const rebooted = yield* make; + const rebooted = yield* ModelManifest.make; assert.deepStrictEqual(yield* rebooted.current, REMOTE_CLAUDE_MANIFEST); }).pipe( Effect.scoped, @@ -523,18 +532,18 @@ describe("ModelManifest service", () => { ]) { yield* fs.writeFileString( cachePath, - yield* encodeManifestCache({ fetchedAtMs: 0, manifest: stale }), + yield* ModelManifest.encodeManifestCache({ fetchedAtMs: 0, manifest: stale }), ); - const service = yield* make; - assert.deepStrictEqual(yield* service.current, BUNDLED_MODEL_MANIFEST); + const service = yield* ModelManifest.make; + assert.deepStrictEqual(yield* service.current, ModelManifest.BUNDLED_MODEL_MANIFEST); } // A cache of a newer edit still outranks the bundle. yield* fs.writeFileString( cachePath, - yield* encodeManifestCache({ fetchedAtMs: 0, manifest: REMOTE_MANIFEST }), + yield* ModelManifest.encodeManifestCache({ fetchedAtMs: 0, manifest: REMOTE_MANIFEST }), ); - const later = yield* make; + const later = yield* ModelManifest.make; assert.deepStrictEqual(yield* later.current, REMOTE_MANIFEST); }).pipe( Effect.scoped, @@ -550,7 +559,7 @@ describe("ModelManifest service", () => { it.live("does not fetch when provider update checks are disabled", () => Effect.gen(function* () { let fetchCount = 0; - const service = yield* make.pipe( + const service = yield* ModelManifest.make.pipe( Effect.provide( httpClientLayer(() => { fetchCount += 1; @@ -558,8 +567,8 @@ describe("ModelManifest service", () => { }), ), ); - assert.deepStrictEqual(yield* service.refresh, BUNDLED_MODEL_MANIFEST); - assert.deepStrictEqual(yield* service.forceRefresh, BUNDLED_MODEL_MANIFEST); + assert.deepStrictEqual(yield* service.refresh, ModelManifest.BUNDLED_MODEL_MANIFEST); + assert.deepStrictEqual(yield* service.forceRefresh, ModelManifest.BUNDLED_MODEL_MANIFEST); assert.strictEqual(fetchCount, 0); }).pipe( Effect.scoped, @@ -575,7 +584,7 @@ describe("ModelManifest service", () => { }); it.effect("caches valid compatibility policies and keeps them after a malformed refresh", () => { - const remote: ModelManifestData = { + const remote: ModelManifest.ModelManifestData = { ...REMOTE_MANIFEST, compatibility: [ { @@ -588,12 +597,12 @@ it.effect("caches valid compatibility policies and keeps them after a malformed }; let invalid = false; return Effect.gen(function* () { - const service = yield* make; + const service = yield* ModelManifest.make; assert.deepStrictEqual((yield* service.refresh).compatibility, remote.compatibility); invalid = true; yield* TestClock.adjust("1 hour"); assert.deepStrictEqual((yield* service.refresh).compatibility, remote.compatibility); - const rebooted = yield* make; + const rebooted = yield* ModelManifest.make; assert.deepStrictEqual((yield* rebooted.current).compatibility, remote.compatibility); }).pipe( Effect.scoped, diff --git a/apps/server/src/provider/OpenCodeServerOwner.test.ts b/apps/server/src/provider/OpenCodeServerOwner.test.ts index 5e66f0a56356..e789e2bec719 100644 --- a/apps/server/src/provider/OpenCodeServerOwner.test.ts +++ b/apps/server/src/provider/OpenCodeServerOwner.test.ts @@ -7,16 +7,12 @@ import * as Ref from "effect/Ref"; import * as TestClock from "effect/testing/TestClock"; import { expect } from "vite-plus/test"; -import { - OpenCodeRuntime, - OpenCodeRuntimeError, - type OpenCodeRuntimeShape, -} from "./opencodeRuntime.ts"; +import * as OpenCodeRuntime from "./opencodeRuntime.ts"; import * as OpenCodeServerOwner from "./OpenCodeServerOwner.ts"; const unusedRuntimeMethod = () => Effect.fail( - new OpenCodeRuntimeError({ + new OpenCodeRuntime.OpenCodeRuntimeError({ operation: "unused", detail: "unused test method", }), @@ -28,11 +24,11 @@ const makeRuntime = Effect.gen(function* () { const failNextStart = yield* Ref.make(false); const started = yield* Deferred.make(); const closed = yield* Deferred.make(); - const runtime: OpenCodeRuntimeShape = { + const runtime: OpenCodeRuntime.OpenCodeRuntimeShape = { startOpenCodeServerProcess: () => Effect.gen(function* () { if (yield* Ref.getAndSet(failNextStart, false)) { - return yield* new OpenCodeRuntimeError({ + return yield* new OpenCodeRuntime.OpenCodeRuntimeError({ operation: "startOpenCodeServerProcess", detail: "start failed", }); @@ -87,7 +83,7 @@ it.effect("shares concurrent borrowers and closes after the idle TTL", () => yield* Deferred.await(testRuntime.closed); expect(yield* Ref.get(testRuntime.closes)).toBe(1); }), - ).pipe(Effect.provideService(OpenCodeRuntime, testRuntime.runtime)); + ).pipe(Effect.provideService(OpenCodeRuntime.OpenCodeRuntime, testRuntime.runtime)); }).pipe(Effect.provide(TestClock.layer())), ); @@ -108,7 +104,7 @@ it.effect("retries a failed start and closes on owner scope shutdown", () => "http://127.0.0.1:1", ); }), - ).pipe(Effect.provideService(OpenCodeRuntime, testRuntime.runtime)); + ).pipe(Effect.provideService(OpenCodeRuntime.OpenCodeRuntime, testRuntime.runtime)); expect(yield* Ref.get(testRuntime.starts)).toBe(1); expect(yield* Ref.get(testRuntime.closes)).toBe(1); }), @@ -119,7 +115,7 @@ it.effect("invalidates an exited process so the next borrower starts a new one", const starts = yield* Ref.make(0); const processExits: Array> = []; const processClosed = yield* Deferred.make(); - const runtime: OpenCodeRuntimeShape = { + const runtime: OpenCodeRuntime.OpenCodeRuntimeShape = { startOpenCodeServerProcess: () => Effect.gen(function* () { const index = yield* Ref.updateAndGet(starts, (count) => count + 1); @@ -159,7 +155,7 @@ it.effect("invalidates an exited process so the next borrower starts a new one", "http://127.0.0.1:2", ); }), - ).pipe(Effect.provideService(OpenCodeRuntime, runtime)); + ).pipe(Effect.provideService(OpenCodeRuntime.OpenCodeRuntime, runtime)); expect(yield* Ref.get(starts)).toBe(2); }), ); @@ -169,7 +165,7 @@ it.effect("replaces a dead cached process before its exit watcher runs", () => const starts = yield* Ref.make(0); const closes = yield* Ref.make(0); const processRunning: Array> = []; - const runtime: OpenCodeRuntimeShape = { + const runtime: OpenCodeRuntime.OpenCodeRuntimeShape = { startOpenCodeServerProcess: () => Effect.gen(function* () { const index = yield* Ref.updateAndGet(starts, (count) => count + 1); @@ -209,7 +205,7 @@ it.effect("replaces a dead cached process before its exit watcher runs", () => expect(yield* Ref.get(starts)).toBe(2); expect(yield* Ref.get(closes)).toBe(1); }), - ).pipe(Effect.provideService(OpenCodeRuntime, runtime)); + ).pipe(Effect.provideService(OpenCodeRuntime.OpenCodeRuntime, runtime)); }), ); @@ -218,7 +214,7 @@ it.effect("cleans up an interrupted startup and allows a retry", () => const starts = yield* Ref.make(0); const firstStartEntered = yield* Deferred.make(); const firstStartClosed = yield* Deferred.make(); - const runtime: OpenCodeRuntimeShape = { + const runtime: OpenCodeRuntime.OpenCodeRuntimeShape = { startOpenCodeServerProcess: () => Effect.gen(function* () { const index = yield* Ref.updateAndGet(starts, (count) => count + 1); @@ -263,7 +259,7 @@ it.effect("cleans up an interrupted startup and allows a retry", () => "http://127.0.0.1:2", ); }), - ).pipe(Effect.provideService(OpenCodeRuntime, runtime)); + ).pipe(Effect.provideService(OpenCodeRuntime.OpenCodeRuntime, runtime)); }), ); @@ -288,6 +284,6 @@ it.effect("releases an interrupted borrower and closes after the idle TTL", () = yield* Deferred.await(testRuntime.closed); expect(yield* Ref.get(testRuntime.closes)).toBe(1); }), - ).pipe(Effect.provideService(OpenCodeRuntime, testRuntime.runtime)); + ).pipe(Effect.provideService(OpenCodeRuntime.OpenCodeRuntime, testRuntime.runtime)); }).pipe(Effect.provide(TestClock.layer())), ); diff --git a/apps/server/src/provider/ProviderCredentialStore.test.ts b/apps/server/src/provider/ProviderCredentialStore.test.ts index 0a5ad906e126..780d39c777fb 100644 --- a/apps/server/src/provider/ProviderCredentialStore.test.ts +++ b/apps/server/src/provider/ProviderCredentialStore.test.ts @@ -1,13 +1,13 @@ import { assert, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; -import { ServerSecretStore } from "../auth/ServerSecretStore.ts"; +import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; import * as ProviderCredentialStore from "./ProviderCredentialStore.ts"; it.effect("isolates provider bindings and preserves opaque credentials", () => Effect.gen(function* () { const data = new Map(); - const secretStore = ServerSecretStore.of({ + const secretStore = ServerSecretStore.ServerSecretStore.of({ get: (name) => Effect.sync(() => Option.fromUndefinedOr(data.get(name))), set: (name, value) => Effect.sync(() => { @@ -21,13 +21,13 @@ it.effect("isolates provider bindings and preserves opaque credentials", () => getOrCreateRandom: () => Effect.die("unused"), }); const a = yield* ProviderCredentialStore.make("cursor", "../../personal").pipe( - Effect.provideService(ServerSecretStore, secretStore), + Effect.provideService(ServerSecretStore.ServerSecretStore, secretStore), ); const b = yield* ProviderCredentialStore.make("cursor", "work").pipe( - Effect.provideService(ServerSecretStore, secretStore), + Effect.provideService(ServerSecretStore.ServerSecretStore, secretStore), ); const c = yield* ProviderCredentialStore.make("other", "../../personal").pipe( - Effect.provideService(ServerSecretStore, secretStore), + Effect.provideService(ServerSecretStore.ServerSecretStore, secretStore), ); const bytes = Uint8Array.from([0, 255, 128, 1]); yield* a.set(bytes); @@ -36,11 +36,11 @@ it.effect("isolates provider bindings and preserves opaque credentials", () => assert.isTrue(Option.isNone(yield* c.get)); assert.isFalse(a.binding.key.includes("/")); const long = yield* ProviderCredentialStore.make("a".repeat(64), "b".repeat(64)).pipe( - Effect.provideService(ServerSecretStore, secretStore), + Effect.provideService(ServerSecretStore.ServerSecretStore, secretStore), ); assert.isBelow(long.binding.key.length, 255); const delimiter = yield* ProviderCredentialStore.make("cur", "sor../../personal").pipe( - Effect.provideService(ServerSecretStore, secretStore), + Effect.provideService(ServerSecretStore.ServerSecretStore, secretStore), ); assert.notStrictEqual(delimiter.binding.key, a.binding.key); yield* a.remove; diff --git a/apps/server/src/provider/acp/AcpRegistryAuth.test.ts b/apps/server/src/provider/acp/AcpRegistryAuth.test.ts index 5f7281be24f2..ddba6a54c072 100644 --- a/apps/server/src/provider/acp/AcpRegistryAuth.test.ts +++ b/apps/server/src/provider/acp/AcpRegistryAuth.test.ts @@ -13,9 +13,9 @@ import * as Stream from "effect/Stream"; import type * as AcpSchema from "effect-acp/compat"; import { AcpRequestError } from "effect-acp/errors"; -import { PtyAdapter, type PtyExitEvent, type PtySpawnInput } from "../../terminal/PtyAdapter.ts"; +import * as PtyAdapter from "../../terminal/PtyAdapter.ts"; import { makeAcpRegistryAuth } from "./AcpRegistryAuth.ts"; -import { AcpRegistryCatalog, type ResolvedAcpRegistryAgent } from "./AcpRegistrySupport.ts"; +import * as AcpRegistrySupport from "./AcpRegistrySupport.ts"; import type { AcpSessionRuntime } from "./AcpSessionRuntime.ts"; const decodeSettings = Schema.decodeSync(AcpRegistrySettings); @@ -28,7 +28,7 @@ const terminalMethod = { args: ["login"], env: { LOGIN: "yes", OVERRIDE: "method" }, }; -const resolved: ResolvedAcpRegistryAgent = { +const resolved: AcpRegistrySupport.ResolvedAcpRegistryAgent = { agent: { id: "test-agent", name: "Test", @@ -43,7 +43,7 @@ const resolved: ResolvedAcpRegistryAgent = { env: { OVERRIDE: "spawn", AGENT_HOME: "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/custom/home" }, }, }; -const catalog: AcpRegistryCatalog["Service"] = { +const catalog: AcpRegistrySupport.AcpRegistryCatalog["Service"] = { inspect: () => Effect.succeed({ status: "ready", @@ -66,9 +66,9 @@ const makeHarness = (method: AcpSchema.AuthMethod, failVerification = false) => const started: number[] = []; let runtimes = 0; let version = "1.0.0"; - let terminalSpawn: PtySpawnInput | undefined; + let terminalSpawn: PtyAdapter.PtySpawnInput | undefined; let data: ((data: string) => void) | undefined; - let exit: ((event: PtyExitEvent) => void) | undefined; + let exit: ((event: PtyAdapter.PtyExitEvent) => void) | undefined; let killed = false; let closedBeforeTerminal = false; const written: string[] = []; @@ -140,7 +140,7 @@ const makeHarness = (method: AcpSchema.AuthMethod, failVerification = false) => }; }), }).pipe( - Effect.provideService(AcpRegistryCatalog, { + Effect.provideService(AcpRegistrySupport.AcpRegistryCatalog, { ...catalog, inspect: () => Effect.sync(() => ({ @@ -150,7 +150,7 @@ const makeHarness = (method: AcpSchema.AuthMethod, failVerification = false) => distribution: "binary" as const, })), }), - Effect.provideService(PtyAdapter, { + Effect.provideService(PtyAdapter.PtyAdapter, { spawn: (input) => Effect.sync(() => { terminalSpawn = input; @@ -209,7 +209,7 @@ const makeHarness = (method: AcpSchema.AuthMethod, failVerification = false) => version = value; }, }; - }).pipe(Effect.provideService(AcpRegistryCatalog, catalog)); + }).pipe(Effect.provideService(AcpRegistrySupport.AcpRegistryCatalog, catalog)); it.effect( "discovers methods without signing in, then waits for browser consent and session verification", diff --git a/apps/server/src/provider/acp/AcpRegistryAuth.ts b/apps/server/src/provider/acp/AcpRegistryAuth.ts index be4008262958..4befdb1cec40 100644 --- a/apps/server/src/provider/acp/AcpRegistryAuth.ts +++ b/apps/server/src/provider/acp/AcpRegistryAuth.ts @@ -16,7 +16,7 @@ import { ChildProcessSpawner } from "effect/unstable/process"; import type * as AcpSchema from "effect-acp/compat"; import * as PtyAdapter from "../../terminal/PtyAdapter.ts"; -import { make as makeProviderAuthFlow, type ProviderAuthFlowContext } from "../ProviderAuthFlow.ts"; +import * as ProviderAuthFlow from "../ProviderAuthFlow.ts"; import { normalizeAcpRegistryAuthMethods, normalizeAcpRegistryWebUrl } from "./AcpRegistryProbe.ts"; import * as AcpRegistrySupport from "./AcpRegistrySupport.ts"; import * as AcpRegistryRuntimeCoordinator from "./AcpRegistryRuntimeCoordinator.ts"; @@ -157,7 +157,7 @@ export const makeAcpRegistryAuth = Effect.fn("makeAcpRegistryAuth")(function* (o const runTerminal = Effect.fnUntraced(function* ( resolved: AcpRegistrySupport.ResolvedAcpRegistryAgent, method: Extract, - context: ProviderAuthFlowContext, + context: ProviderAuthFlow.ProviderAuthFlowContext, ) { if (Option.isNone(pty)) return yield* failure( @@ -228,7 +228,7 @@ export const makeAcpRegistryAuth = Effect.fn("makeAcpRegistryAuth")(function* (o return yield* failure("start", "The provider login command did not finish successfully."); }); - const authenticate = (methodId: string, context: ProviderAuthFlowContext) => + const authenticate = (methodId: string, context: ProviderAuthFlow.ProviderAuthFlowContext) => Effect.gen(function* () { if (!options.settings.enabled) return yield* failure("start", "Enable this provider before signing in."); @@ -357,7 +357,7 @@ export const makeAcpRegistryAuth = Effect.fn("makeAcpRegistryAuth")(function* (o ? coordinator.value.withForegroundStartup(options.settings.agentId, logout) : logout ).pipe(Effect.andThen(options.onChanged(false))); - return yield* makeProviderAuthFlow({ + return yield* ProviderAuthFlow.make({ instanceId: options.instanceId, // ACP doesn't advertise its credential scope. Conservatively treat all // instances of the same agent on this environment as sharing credentials. diff --git a/apps/server/src/provider/acp/AcpRegistryProbe.test.ts b/apps/server/src/provider/acp/AcpRegistryProbe.test.ts index 2a4d28eedc13..4f7422ff783c 100644 --- a/apps/server/src/provider/acp/AcpRegistryProbe.test.ts +++ b/apps/server/src/provider/acp/AcpRegistryProbe.test.ts @@ -22,7 +22,7 @@ import { normalizeAcpRegistryCommands, probeAcpRegistryConfiguration, } from "./AcpRegistryProbe.ts"; -import { AcpRegistryCatalog } from "./AcpRegistrySupport.ts"; +import * as AcpRegistrySupport from "./AcpRegistrySupport.ts"; const instanceId = ProviderInstanceId.make("acpRegistry_codex"); const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); @@ -324,8 +324,8 @@ describe("ACP Registry probe", () => { ]); }).pipe( Effect.provideService( - AcpRegistryCatalog, - AcpRegistryCatalog.of({ + AcpRegistrySupport.AcpRegistryCatalog, + AcpRegistrySupport.AcpRegistryCatalog.of({ search: () => Effect.die("unused search"), prepare: () => Effect.die("unused prepare"), inspect: () => Effect.die("unused inspect"), @@ -359,7 +359,7 @@ describe("ACP Registry probe", () => { it.effect("includes package resolution in the probe timeout", () => Effect.gen(function* () { const resolveStarted = yield* Deferred.make(); - const catalog = AcpRegistryCatalog.of({ + const catalog = AcpRegistrySupport.AcpRegistryCatalog.of({ search: () => Effect.die("unused search"), prepare: () => Effect.die("unused prepare"), inspect: () => Effect.die("unused inspect"), @@ -373,7 +373,7 @@ describe("ACP Registry probe", () => { cwd: process.cwd(), environment: process.env, }).pipe( - Effect.provideService(AcpRegistryCatalog, catalog), + Effect.provideService(AcpRegistrySupport.AcpRegistryCatalog, catalog), Effect.flip, Effect.forkChild({ startImmediately: true }), ); @@ -389,7 +389,7 @@ describe("ACP Registry probe", () => { ); it.effect("lists native sessions and logs out through generic ACP lifecycle methods", () => { - const catalog = AcpRegistryCatalog.of({ + const catalog = AcpRegistrySupport.AcpRegistryCatalog.of({ search: () => Effect.die("unused search"), prepare: () => Effect.die("unused prepare"), inspect: () => Effect.die("unused inspect"), @@ -438,14 +438,14 @@ describe("ACP Registry probe", () => { }); yield* logoutAcpRegistry(input); }).pipe( - Effect.provideService(AcpRegistryCatalog, catalog), + Effect.provideService(AcpRegistrySupport.AcpRegistryCatalog, catalog), Effect.provide(NodeServices.layer), Effect.scoped, ); }); it.effect("preserves the ACP method error for unsupported session management", () => { - const catalog = AcpRegistryCatalog.of({ + const catalog = AcpRegistrySupport.AcpRegistryCatalog.of({ search: () => Effect.die("unused search"), prepare: () => Effect.die("unused prepare"), inspect: () => Effect.die("unused inspect"), @@ -482,7 +482,7 @@ describe("ACP Registry probe", () => { cause: { _tag: "AcpRequestError", code: -32601 }, }); }).pipe( - Effect.provideService(AcpRegistryCatalog, catalog), + Effect.provideService(AcpRegistrySupport.AcpRegistryCatalog, catalog), Effect.provide(NodeServices.layer), Effect.scoped, ); diff --git a/apps/server/src/provider/acp/AcpRegistryProbe.ts b/apps/server/src/provider/acp/AcpRegistryProbe.ts index 018a3f0e7eb8..588a04e97629 100644 --- a/apps/server/src/provider/acp/AcpRegistryProbe.ts +++ b/apps/server/src/provider/acp/AcpRegistryProbe.ts @@ -23,11 +23,11 @@ import { ChildProcessSpawner } from "effect/unstable/process"; import type * as EffectAcpErrors from "effect-acp/errors"; import type * as EffectAcpSchema from "effect-acp/compat"; -import { PtyAdapter } from "../../terminal/PtyAdapter.ts"; -import { AcpRegistryCatalog, toAcpRegistryOperationError } from "./AcpRegistrySupport.ts"; +import * as PtyAdapter from "../../terminal/PtyAdapter.ts"; +import * as AcpRegistrySupport from "./AcpRegistrySupport.ts"; import { parseSessionModeState } from "./AcpRuntimeModel.ts"; import { acpProviderOptionDescriptors } from "./AcpSessionConfig.ts"; -import { AcpRegistryRuntimeCoordinator } from "./AcpRegistryRuntimeCoordinator.ts"; +import * as AcpRegistryRuntimeCoordinator from "./AcpRegistryRuntimeCoordinator.ts"; import * as AcpSessionRuntime from "./AcpSessionRuntime.ts"; const MAX_AUTH_METHODS = 32; @@ -313,12 +313,12 @@ export const probeAcpRegistryConfiguration = Effect.fn("AcpRegistryProbe.probeCo ): Effect.fn.Return< AcpRegistryConfigurationProbeResult, AcpRegistryOperationError, - AcpRegistryCatalog | ChildProcessSpawner.ChildProcessSpawner | Crypto.Crypto + AcpRegistrySupport.AcpRegistryCatalog | ChildProcessSpawner.ChildProcessSpawner | Crypto.Crypto > { - const catalog = yield* AcpRegistryCatalog; + const catalog = yield* AcpRegistrySupport.AcpRegistryCatalog; const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const crypto = yield* Crypto.Crypto; - const pty = yield* Effect.serviceOption(PtyAdapter); + const pty = yield* Effect.serviceOption(PtyAdapter.PtyAdapter); const result = yield* Effect.gen(function* () { // Resolution may install a missing registry package. Keep it inside the @@ -326,10 +326,12 @@ export const probeAcpRegistryConfiguration = Effect.fn("AcpRegistryProbe.probeCo // much longer timeout. const resolved = yield* catalog .resolve(input.settings, input.cwd, input.environment) - .pipe(Effect.mapError(toAcpRegistryOperationError)); + .pipe(Effect.mapError(AcpRegistrySupport.toAcpRegistryOperationError)); const authMethodsRef = yield* Ref.make>([]); const authActionRef = yield* Ref.make(undefined); - const runtimeCoordinator = yield* Effect.serviceOption(AcpRegistryRuntimeCoordinator); + const runtimeCoordinator = yield* Effect.serviceOption( + AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator, + ); const runtimeContext = yield* Layer.build( AcpSessionRuntime.layer({ spawn: resolved.spawn, @@ -454,13 +456,15 @@ const MANAGEMENT_TIMEOUT = Duration.seconds(60); const makeAcpRegistryManagementRuntime = Effect.fn("AcpRegistryProbe.makeManagementRuntime")( function* (input: AcpRegistryManagementInput) { - const catalog = yield* AcpRegistryCatalog; + const catalog = yield* AcpRegistrySupport.AcpRegistryCatalog; const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const crypto = yield* Crypto.Crypto; - const runtimeCoordinator = yield* Effect.serviceOption(AcpRegistryRuntimeCoordinator); + const runtimeCoordinator = yield* Effect.serviceOption( + AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator, + ); const resolved = yield* catalog .resolve(input.settings, input.cwd, input.environment) - .pipe(Effect.mapError(toAcpRegistryOperationError)); + .pipe(Effect.mapError(AcpRegistrySupport.toAcpRegistryOperationError)); const runtimeContext = yield* Layer.build( AcpSessionRuntime.layer({ spawn: resolved.spawn, diff --git a/apps/server/src/provider/acp/AcpRegistryRuntimeCoordinator.test.ts b/apps/server/src/provider/acp/AcpRegistryRuntimeCoordinator.test.ts index ee40c30c3a52..619d794db833 100644 --- a/apps/server/src/provider/acp/AcpRegistryRuntimeCoordinator.test.ts +++ b/apps/server/src/provider/acp/AcpRegistryRuntimeCoordinator.test.ts @@ -10,12 +10,12 @@ import type { AcpRegistryAvailableCommands, AcpRegistryLiveConfiguration, } from "./AcpRegistryProbe.ts"; -import { AcpRegistryRuntimeCoordinator } from "./AcpRegistryRuntimeCoordinator.ts"; +import * as AcpRegistryRuntimeCoordinator from "./AcpRegistryRuntimeCoordinator.ts"; describe("AcpRegistryRuntimeCoordinator", () => { it.effect("suppresses a background probe while foreground startup is active", () => Effect.gen(function* () { - const coordinator = yield* AcpRegistryRuntimeCoordinator; + const coordinator = yield* AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator; const foregroundStarted = yield* Deferred.make(); const releaseForeground = yield* Deferred.make(); const foreground = yield* coordinator @@ -33,12 +33,15 @@ describe("AcpRegistryRuntimeCoordinator", () => { yield* Deferred.succeed(releaseForeground, undefined); yield* Fiber.join(foreground); - }).pipe(Effect.provide(AcpRegistryRuntimeCoordinator.layer), Effect.scoped), + }).pipe( + Effect.provide(AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator.layer), + Effect.scoped, + ), ); it.effect("interrupts an active background probe when foreground startup begins", () => Effect.gen(function* () { - const coordinator = yield* AcpRegistryRuntimeCoordinator; + const coordinator = yield* AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator; const probeStarted = yield* Deferred.make(); const probeFinalized = yield* Deferred.make(); const releaseForeground = yield* Deferred.make(); @@ -61,12 +64,15 @@ describe("AcpRegistryRuntimeCoordinator", () => { yield* Deferred.succeed(releaseForeground, undefined); yield* Fiber.join(foreground); - }).pipe(Effect.provide(AcpRegistryRuntimeCoordinator.layer), Effect.scoped), + }).pipe( + Effect.provide(AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator.layer), + Effect.scoped, + ), ); it.effect("serializes native session mutations across callers", () => Effect.gen(function* () { - const coordinator = yield* AcpRegistryRuntimeCoordinator; + const coordinator = yield* AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator; const firstEntered = yield* Deferred.make(); const releaseFirst = yield* Deferred.make(); const secondEntered = yield* Deferred.make(); @@ -88,12 +94,15 @@ describe("AcpRegistryRuntimeCoordinator", () => { yield* Fiber.join(first); yield* Fiber.join(second); expect(Option.isSome(yield* Deferred.poll(secondEntered))).toBe(true); - }).pipe(Effect.provide(AcpRegistryRuntimeCoordinator.layer), Effect.scoped), + }).pipe( + Effect.provide(AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator.layer), + Effect.scoped, + ), ); it.effect("replays and replaces late command advertisements per provider instance", () => Effect.gen(function* () { - const coordinator = yield* AcpRegistryRuntimeCoordinator; + const coordinator = yield* AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator; const codex = ProviderInstanceId.make("acpRegistry_codex"); const kilo = ProviderInstanceId.make("acpRegistry_kilo"); const firstSeen = yield* Deferred.make(); @@ -136,12 +145,15 @@ describe("AcpRegistryRuntimeCoordinator", () => { ); yield* coordinator.clearAvailableCommands(codex); expect(Option.isNone(yield* coordinator.getAvailableCommands(codex))).toBe(true); - }).pipe(Effect.provide(AcpRegistryRuntimeCoordinator.layer), Effect.scoped), + }).pipe( + Effect.provide(AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator.layer), + Effect.scoped, + ), ); it.effect("replays and replaces live configuration per provider instance", () => Effect.gen(function* () { - const coordinator = yield* AcpRegistryRuntimeCoordinator; + const coordinator = yield* AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator; const instanceId = ProviderInstanceId.make("acpRegistry_kilo"); const firstSeen = yield* Deferred.make(); const replacementSeen = yield* Deferred.make(); @@ -177,12 +189,15 @@ describe("AcpRegistryRuntimeCoordinator", () => { expect(yield* coordinator.getLiveConfiguration(instanceId)).toEqual(Option.some(replacement)); yield* coordinator.clearLiveConfiguration(instanceId); expect(Option.isNone(yield* coordinator.getLiveConfiguration(instanceId))).toBe(true); - }).pipe(Effect.provide(AcpRegistryRuntimeCoordinator.layer), Effect.scoped), + }).pipe( + Effect.provide(AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator.layer), + Effect.scoped, + ), ); it.effect("requires matching user consent before accepting URL authentication", () => Effect.gen(function* () { - const coordinator = yield* AcpRegistryRuntimeCoordinator; + const coordinator = yield* AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator; const instanceId = ProviderInstanceId.make("acpRegistry_antigravity"); const action = { elicitationId: "login-1", @@ -219,12 +234,15 @@ describe("AcpRegistryRuntimeCoordinator", () => { expect(yield* Fiber.join(request)).toBe(true); expect(Option.isNone(yield* coordinator.getUrlAuthAction(instanceId))).toBe(true); yield* Fiber.interrupt(consumer); - }).pipe(Effect.provide(AcpRegistryRuntimeCoordinator.layer), Effect.scoped), + }).pipe( + Effect.provide(AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator.layer), + Effect.scoped, + ), ); it.effect("expires stale URL authentication actions without accepting them", () => Effect.gen(function* () { - const coordinator = yield* AcpRegistryRuntimeCoordinator; + const coordinator = yield* AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator; const instanceId = ProviderInstanceId.make("acpRegistry_expired"); const actionSeen = yield* Deferred.make(); const consumer = yield* coordinator @@ -253,12 +271,15 @@ describe("AcpRegistryRuntimeCoordinator", () => { ).toBe(false); expect(Option.isNone(yield* coordinator.getUrlAuthAction(instanceId))).toBe(true); yield* Fiber.interrupt(consumer); - }).pipe(Effect.provide(AcpRegistryRuntimeCoordinator.layer), Effect.scoped), + }).pipe( + Effect.provide(AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator.layer), + Effect.scoped, + ), ); it.effect("publishes a replacement URL action before retiring the previous request", () => Effect.gen(function* () { - const coordinator = yield* AcpRegistryRuntimeCoordinator; + const coordinator = yield* AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator; const instanceId = ProviderInstanceId.make("acpRegistry_replacement"); const received: Array = []; const firstSeen = yield* Deferred.make(); @@ -305,12 +326,15 @@ describe("AcpRegistryRuntimeCoordinator", () => { ).toBe(true); expect(yield* Fiber.join(replacement)).toBe(true); yield* Fiber.interrupt(consumer); - }).pipe(Effect.provide(AcpRegistryRuntimeCoordinator.layer), Effect.scoped), + }).pipe( + Effect.provide(AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator.layer), + Effect.scoped, + ), ); it.effect("clears a published URL action when its request is interrupted", () => Effect.gen(function* () { - const coordinator = yield* AcpRegistryRuntimeCoordinator; + const coordinator = yield* AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator; const instanceId = ProviderInstanceId.make("acpRegistry_interrupted"); const actionSeen = yield* Deferred.make(); const cleared = yield* Deferred.make(); @@ -334,6 +358,9 @@ describe("AcpRegistryRuntimeCoordinator", () => { yield* Deferred.await(cleared); expect(Option.isNone(yield* coordinator.getUrlAuthAction(instanceId))).toBe(true); yield* Fiber.interrupt(consumer); - }).pipe(Effect.provide(AcpRegistryRuntimeCoordinator.layer), Effect.scoped), + }).pipe( + Effect.provide(AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator.layer), + Effect.scoped, + ), ); }); diff --git a/apps/server/src/provider/acp/AcpRegistrySupport.test.ts b/apps/server/src/provider/acp/AcpRegistrySupport.test.ts index c276dcd8362a..1a5172d4a99d 100644 --- a/apps/server/src/provider/acp/AcpRegistrySupport.test.ts +++ b/apps/server/src/provider/acp/AcpRegistrySupport.test.ts @@ -21,22 +21,16 @@ import * as TestClock from "effect/testing/TestClock"; import * as NodeCrypto from "node:crypto"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; -import { - AcpRegistryError, - acpRegistryManagedBinaryDirectories, - makeAcpRegistryCatalog, - resolveAcpRegistryDistribution, - resolveAcpRegistryPlatformTarget, - toAcpRegistryOperationError, - type AcpRegistryAgent, -} from "./AcpRegistrySupport.ts"; +import * as AcpRegistrySupport from "./AcpRegistrySupport.ts"; const registryUrl = "https://registry.test/registry.json"; const archiveUrl = "https://registry.test/example-agent.bin"; const decodeAcpRegistrySettings = Schema.decodeSync(AcpRegistrySettings); const encodeUnknownJson = Schema.encodeUnknownSync(Schema.fromJsonString(Schema.Unknown)); -function makeAgent(distribution: AcpRegistryAgent["distribution"]): AcpRegistryAgent { +function makeAgent( + distribution: AcpRegistrySupport.AcpRegistryAgent["distribution"], +): AcpRegistrySupport.AcpRegistryAgent { return { id: "example-agent", name: "Example Agent", @@ -46,7 +40,7 @@ function makeAgent(distribution: AcpRegistryAgent["distribution"]): AcpRegistryA }; } -function makeRegistry(agent: AcpRegistryAgent): string { +function makeRegistry(agent: AcpRegistrySupport.AcpRegistryAgent): string { return JSON.stringify({ version: "1.0.0", agents: [agent] }); } @@ -190,13 +184,13 @@ const makeFakeUvToolchain = Effect.fn("AcpRegistrySupport.test.makeFakeUvToolcha describe("AcpRegistrySupport", () => { it("preserves the registry failure when translating it for clients", () => { const cause = new Error("registry unavailable"); - const failure = new AcpRegistryError({ + const failure = new AcpRegistrySupport.AcpRegistryError({ reason: "registry_unavailable", detail: "Could not load the ACP Registry.", cause, }); - expect(toAcpRegistryOperationError(failure)).toMatchObject({ + expect(AcpRegistrySupport.toAcpRegistryOperationError(failure)).toMatchObject({ reason: "registry_unavailable", message: "Could not load the ACP Registry.", cause: failure, @@ -204,11 +198,17 @@ describe("AcpRegistrySupport", () => { }); it("maps supported Node platforms to ACP Registry target keys", () => { - expect(resolveAcpRegistryPlatformTarget("darwin", "arm64")).toBe("darwin-aarch64"); - expect(resolveAcpRegistryPlatformTarget("linux", "x64")).toBe("linux-x86_64"); - expect(resolveAcpRegistryPlatformTarget("win32", "arm64")).toBe("windows-aarch64"); - expect(resolveAcpRegistryPlatformTarget("freebsd", "x64")).toBeUndefined(); - expect(resolveAcpRegistryPlatformTarget("linux", "ia32")).toBeUndefined(); + expect(AcpRegistrySupport.resolveAcpRegistryPlatformTarget("darwin", "arm64")).toBe( + "darwin-aarch64", + ); + expect(AcpRegistrySupport.resolveAcpRegistryPlatformTarget("linux", "x64")).toBe( + "linux-x86_64", + ); + expect(AcpRegistrySupport.resolveAcpRegistryPlatformTarget("win32", "arm64")).toBe( + "windows-aarch64", + ); + expect(AcpRegistrySupport.resolveAcpRegistryPlatformTarget("freebsd", "x64")).toBeUndefined(); + expect(AcpRegistrySupport.resolveAcpRegistryPlatformTarget("linux", "ia32")).toBeUndefined(); }); it("selects the preferred compatible distribution", () => { @@ -227,14 +227,14 @@ describe("AcpRegistrySupport", () => { }); expect( - resolveAcpRegistryDistribution({ + AcpRegistrySupport.resolveAcpRegistryDistribution({ agent, preference: "auto", platformTarget: "linux-x86_64", }), ).toMatchObject({ kind: "binary", args: ["acp"] }); expect( - resolveAcpRegistryDistribution({ + AcpRegistrySupport.resolveAcpRegistryDistribution({ agent, preference: "npx", platformTarget: "linux-x86_64", @@ -246,7 +246,7 @@ describe("AcpRegistrySupport", () => { env: {}, }); expect( - resolveAcpRegistryDistribution({ + AcpRegistrySupport.resolveAcpRegistryDistribution({ agent, preference: "binary", platformTarget: "darwin-aarch64", @@ -274,7 +274,7 @@ describe("AcpRegistrySupport", () => { const commandPath = `${cacheDir}/example-agent`; yield* fileSystem.writeFileString(commandPath, "#!/bin/sh\n"); yield* fileSystem.chmod(commandPath, 0o755); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -319,7 +319,7 @@ describe("AcpRegistrySupport", () => { prefix: "t3-acp-registry-global-package-", }); const toolchain = yield* makeFakeNpmToolchain(cacheDir); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -365,7 +365,7 @@ describe("AcpRegistrySupport", () => { HOME: userHome, FAKE_NPM_PREFIX: systemPrefix, }; - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -397,7 +397,7 @@ describe("AcpRegistrySupport", () => { prefix: "t3-acp-registry-global-uv-tool-", }); const toolchain = yield* makeFakeUvToolchain(cacheDir); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -433,7 +433,7 @@ describe("AcpRegistrySupport", () => { prefix: "t3-acp-registry-windows-package-path-", }); const toolchain = yield* makeFakeUvToolchain(cacheDir); - const linuxResolver = yield* makeAcpRegistryCatalog({ + const linuxResolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -445,7 +445,7 @@ describe("AcpRegistrySupport", () => { Path: "C:\\host\\bin", PATH: "C:\\provider\\bin", }; - const windowsResolver = yield* makeAcpRegistryCatalog({ + const windowsResolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -494,7 +494,7 @@ describe("AcpRegistrySupport", () => { const cacheDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-acp-registry-install-", }); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -562,7 +562,7 @@ describe("AcpRegistrySupport", () => { yield* fileSystem.chmod(systemBinary, 0o755); const environment = { PATH: systemBinDir }; - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -635,7 +635,7 @@ describe("AcpRegistrySupport", () => { ); expect(yield* tar.exitCode).toBe(0); const archive = yield* fileSystem.readFile(archivePath); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -680,13 +680,13 @@ describe("AcpRegistrySupport", () => { website: "https://example.test/codex", repository: "https://example.test/codex/source", icon: "https://example.test/codex.svg", - } satisfies AcpRegistryAgent; + } satisfies AcpRegistrySupport.AcpRegistryAgent; const descriptionMatch = { ...makeAgent({ npx: { package: "other-agent@1.2.3" } }), id: "other-agent", name: "Other Agent", description: "An adapter for Codex workflows", - } satisfies AcpRegistryAgent; + } satisfies AcpRegistrySupport.AcpRegistryAgent; const incompatible = { ...makeAgent({ binary: { @@ -695,14 +695,14 @@ describe("AcpRegistrySupport", () => { }), id: "darwin-only", name: "Codex Darwin", - } satisfies AcpRegistryAgent; + } satisfies AcpRegistrySupport.AcpRegistryAgent; return Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const cacheDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-acp-registry-search-", }); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -744,7 +744,7 @@ describe("AcpRegistrySupport", () => { const cacheDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-acp-registry-refresh-", }); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -780,13 +780,13 @@ describe("AcpRegistrySupport", () => { }), id: "binary-agent", name: "Binary Agent", - } satisfies AcpRegistryAgent; + } satisfies AcpRegistrySupport.AcpRegistryAgent; return Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const cacheDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-acp-registry-runner-filter-", }); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -818,7 +818,7 @@ describe("AcpRegistrySupport", () => { binary: { "linux-x86_64": { archive: archiveUrl, cmd: "example-agent" }, }, - } satisfies AcpRegistryAgent["distribution"]; + } satisfies AcpRegistrySupport.AcpRegistryAgent["distribution"]; const valid = makeAgent(distribution); const blankName = { ...valid, id: "blank-name", name: " " }; const blankVersion = { ...valid, id: "blank-version", version: "\t" }; @@ -828,7 +828,7 @@ describe("AcpRegistrySupport", () => { const cacheDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-acp-registry-blank-fields-", }); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -863,7 +863,7 @@ describe("AcpRegistrySupport", () => { const cacheDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-acp-registry-unpinned-", }); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -894,7 +894,7 @@ describe("AcpRegistrySupport", () => { const cacheDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-acp-registry-runner-syntax-", }); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -942,7 +942,7 @@ describe("AcpRegistrySupport", () => { const cacheDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-acp-registry-checksum-", }); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -977,7 +977,7 @@ describe("AcpRegistrySupport", () => { prefix: "t3-acp-registry-runner-", }); const toolchain = yield* makeFakeNpmToolchain(cacheDir); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -1024,7 +1024,7 @@ describe("AcpRegistrySupport", () => { yield* fileSystem.makeDirectory(registryDirectory, { recursive: true }); yield* fileSystem.writeFileString(`${registryDirectory}/registry.json`, makeRegistry(agent)); const toolchain = yield* makeFakeNpmToolchain(cacheDir); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -1062,7 +1062,7 @@ describe("AcpRegistrySupport", () => { const cacheDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-acp-registry-invalid-", }); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -1096,7 +1096,7 @@ describe("AcpRegistrySupport", () => { yield* fileSystem.makeDirectory(registryDirectory, { recursive: true }); yield* fileSystem.writeFileString(`${registryDirectory}/registry.json`, makeRegistry(agent)); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -1125,7 +1125,7 @@ describe("AcpRegistrySupport", () => { const cacheDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-acp-registry-empty-inspect-", }); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -1157,7 +1157,7 @@ describe("AcpRegistrySupport", () => { yield* fileSystem.writeFileString(`${registryDirectory}/registry.json`, makeRegistry(agent)); const toolchain = yield* makeFakeNpmToolchain(cacheDir); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -1205,7 +1205,7 @@ describe("AcpRegistrySupport", () => { yield* fileSystem.makeDirectory(registryDirectory, { recursive: true }); yield* fileSystem.writeFileString(`${registryDirectory}/registry.json`, makeRegistry(agent)); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -1248,7 +1248,7 @@ describe("AcpRegistrySupport", () => { }); yield* fileSystem.writeFileString(runnerCache, "{}"); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -1281,7 +1281,7 @@ describe("AcpRegistrySupport", () => { yield* fileSystem.writeFileString(`${versionRoot}/linux-x86_64/agent`, "binary"); yield* fileSystem.makeDirectory(`${versionRoot}/npm/bin`, { recursive: true }); yield* fileSystem.writeFileString(`${versionRoot}/npm/bin/agent`, "package command"); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -1324,7 +1324,7 @@ describe("AcpRegistrySupport", () => { const cacheDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-acp-registry-uninstall-race-", }); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -1401,7 +1401,7 @@ describe("AcpRegistrySupport", () => { const cacheDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-acp-registry-uninstall-reservation-", }); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -1449,7 +1449,7 @@ describe("AcpRegistrySupport", () => { const cacheDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-acp-registry-uninstall-expiry-", }); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir, toolsDir: `${cacheDir}/tools`, registryUrl, @@ -1556,7 +1556,7 @@ describe("acpRegistryManagedBinaryDirectories", () => { }), ); - const directories = yield* acpRegistryManagedBinaryDirectories({ + const directories = yield* AcpRegistrySupport.acpRegistryManagedBinaryDirectories({ fileSystem, path, cacheDir, @@ -1570,7 +1570,7 @@ describe("acpRegistryManagedBinaryDirectories", () => { path.join(cacheDir, "tools", "kimi", "1.49.0", "linux-x86_64", "bin"), ]); - const missing = yield* acpRegistryManagedBinaryDirectories({ + const missing = yield* AcpRegistrySupport.acpRegistryManagedBinaryDirectories({ fileSystem, path, cacheDir: path.join(cacheDir, "does-not-exist"), diff --git a/apps/server/src/provider/makeManagedServerProvider.test.ts b/apps/server/src/provider/makeManagedServerProvider.test.ts index d01f6306786b..8b6133d5c523 100644 --- a/apps/server/src/provider/makeManagedServerProvider.test.ts +++ b/apps/server/src/provider/makeManagedServerProvider.test.ts @@ -18,7 +18,7 @@ import * as Stream from "effect/Stream"; import { TestClock } from "effect/testing"; import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; +import * as ServerSettings from "../serverSettings.ts"; import { makeManagedServerProvider } from "./makeManagedServerProvider.ts"; const emptyCapabilities = createModelCapabilities({ optionDescriptors: [] }); @@ -132,7 +132,7 @@ function makeBackgroundPolicyLayer(shouldRunScopeWork: boolean) { const BackgroundPolicyAlwaysRunLayer = makeBackgroundPolicyLayer(true); const BackgroundPolicyNeverRunLayer = makeBackgroundPolicyLayer(false); -const ServerSettingsTestLayer = ServerSettingsService.layerTest(); +const ServerSettingsTestLayer = ServerSettings.layerTest(); const AlwaysRunTestLayer = Layer.merge(BackgroundPolicyAlwaysRunLayer, ServerSettingsTestLayer); const NeverRunTestLayer = Layer.merge(BackgroundPolicyNeverRunLayer, ServerSettingsTestLayer); @@ -294,8 +294,8 @@ describe("makeManagedServerProvider", () => { const serverSettingsRef = yield* Ref.make(initialServerSettings); const serverSettingsChanges = yield* PubSub.unbounded(); const serverSettingsLayer = Layer.succeed( - ServerSettingsService, - ServerSettingsService.of({ + ServerSettings.ServerSettingsService, + ServerSettings.ServerSettingsService.of({ start: Effect.void, ready: Effect.void, getSettings: Ref.get(serverSettingsRef), diff --git a/apps/server/src/provider/makeManagedServerProvider.ts b/apps/server/src/provider/makeManagedServerProvider.ts index 119c577474c8..7884b9d1e158 100644 --- a/apps/server/src/provider/makeManagedServerProvider.ts +++ b/apps/server/src/provider/makeManagedServerProvider.ts @@ -16,7 +16,7 @@ import * as Stream from "effect/Stream"; import * as Semaphore from "effect/Semaphore"; import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; +import * as ServerSettings from "../serverSettings.ts"; import { applyUsageLimitsUpdate, resolveUsageLimitsAfterProbe } from "./providerUsageLimits.ts"; import type { ServerProviderShape } from "./Services/ServerProvider.ts"; @@ -57,10 +57,10 @@ export const makeManagedServerProvider = Effect.fn("makeManagedServerProvider")( }): Effect.fn.Return< ServerProviderShape, ServerSettingsError, - Scope.Scope | BackgroundPolicy.BackgroundPolicy | ServerSettingsService + Scope.Scope | BackgroundPolicy.BackgroundPolicy | ServerSettings.ServerSettingsService > { const backgroundPolicy = yield* BackgroundPolicy.BackgroundPolicy; - const serverSettings = yield* ServerSettingsService; + const serverSettings = yield* ServerSettings.ServerSettingsService; const refreshSemaphore = yield* Semaphore.make(1); const changesPubSub = yield* Effect.acquireRelease( PubSub.unbounded(), diff --git a/apps/server/src/provider/opencodeRuntime.environment.test.ts b/apps/server/src/provider/opencodeRuntime.environment.test.ts index f7cf5f310ddd..586b7fb1448d 100644 --- a/apps/server/src/provider/opencodeRuntime.environment.test.ts +++ b/apps/server/src/provider/opencodeRuntime.environment.test.ts @@ -15,20 +15,13 @@ import * as TestClock from "effect/testing/TestClock"; import { FetchHttpClient, HttpClient } from "effect/unstable/http"; import { describe, expect, it } from "vite-plus/test"; -import { - OpenCodeRuntime, - OpenCodeRuntimeError, - OpenCodeRuntimeLive, - resolveOpenCodeConfigContent, - resolveOpenCodeServerPassword, - verifyOpenCodeServerVersion, -} from "./opencodeRuntime.ts"; +import * as OpenCodeRuntime from "./opencodeRuntime.ts"; import * as OpenCodeServerLedger from "./OpenCodeServerLedger.ts"; describe("resolveOpenCodeConfigContent", () => { it("prefers the caller environment over the inherited environment", () => { expect( - resolveOpenCodeConfigContent( + OpenCodeRuntime.resolveOpenCodeConfigContent( { OPENCODE_CONFIG_CONTENT: '{"source":"caller"}' }, { OPENCODE_CONFIG_CONTENT: '{"source":"process"}' }, ), @@ -37,18 +30,18 @@ describe("resolveOpenCodeConfigContent", () => { it("falls back to the inherited environment and then an empty config", () => { expect( - resolveOpenCodeConfigContent(undefined, { + OpenCodeRuntime.resolveOpenCodeConfigContent(undefined, { OPENCODE_CONFIG_CONTENT: '{"source":"process"}', }), ).toBe('{"source":"process"}'); - expect(resolveOpenCodeConfigContent(undefined, {})).toBe("{}"); + expect(OpenCodeRuntime.resolveOpenCodeConfigContent(undefined, {})).toBe("{}"); }); }); describe("resolveOpenCodeServerPassword", () => { it("uses the local environment password when settings do not provide one", () => { expect( - resolveOpenCodeServerPassword( + OpenCodeRuntime.resolveOpenCodeServerPassword( { external: false, environment: { OPENCODE_SERVER_PASSWORD: " env password " } }, {}, ), @@ -57,13 +50,16 @@ describe("resolveOpenCodeServerPassword", () => { it("uses the settings password for a local server", () => { expect( - resolveOpenCodeServerPassword({ external: false, serverPassword: " settings password " }, {}), + OpenCodeRuntime.resolveOpenCodeServerPassword( + { external: false, serverPassword: " settings password " }, + {}, + ), ).toBe(" settings password "); }); it("uses the settings password when local settings and environment differ", () => { expect( - resolveOpenCodeServerPassword( + OpenCodeRuntime.resolveOpenCodeServerPassword( { external: false, serverPassword: "settings-password", @@ -76,7 +72,7 @@ describe("resolveOpenCodeServerPassword", () => { it("does not send an inherited local password to an external server", () => { expect( - resolveOpenCodeServerPassword( + OpenCodeRuntime.resolveOpenCodeServerPassword( { external: true, environment: { OPENCODE_SERVER_PASSWORD: "local-secret" } }, { OPENCODE_SERVER_PASSWORD: "inherited-secret" }, ), @@ -97,7 +93,7 @@ function makeHealthClient( describe("verifyOpenCodeServerVersion", () => { effectIt.effect("accepts a supported server version", () => Effect.gen(function* () { - const version = yield* verifyOpenCodeServerVersion( + const version = yield* OpenCodeRuntime.verifyOpenCodeServerVersion( makeHealthClient(() => Promise.resolve({ data: { healthy: true, version: "1.14.19" } })), ); expect(version).toBe("1.14.19"); @@ -106,10 +102,10 @@ describe("verifyOpenCodeServerVersion", () => { effectIt.effect("rejects a server below the supported version", () => Effect.gen(function* () { - const error = yield* verifyOpenCodeServerVersion( + const error = yield* OpenCodeRuntime.verifyOpenCodeServerVersion( makeHealthClient(() => Promise.resolve({ data: { healthy: true, version: "1.14.18" } })), ).pipe(Effect.flip); - expect(error).toBeInstanceOf(OpenCodeRuntimeError); + expect(error).toBeInstanceOf(OpenCodeRuntime.OpenCodeRuntimeError); expect(error.detail).toContain("v1.14.18 is too old"); }), ); @@ -121,10 +117,10 @@ describe("verifyOpenCodeServerVersion", () => { ]) { effectIt.effect(`rejects an invalid health response: ${JSON.stringify(data)}`, () => Effect.gen(function* () { - const error = yield* verifyOpenCodeServerVersion( + const error = yield* OpenCodeRuntime.verifyOpenCodeServerVersion( makeHealthClient(() => Promise.resolve({ data })), ).pipe(Effect.flip); - expect(error).toBeInstanceOf(OpenCodeRuntimeError); + expect(error).toBeInstanceOf(OpenCodeRuntime.OpenCodeRuntimeError); expect(error.detail).toContain("requires OpenCode v1.14.19 or newer"); }), ); @@ -132,12 +128,12 @@ describe("verifyOpenCodeServerVersion", () => { effectIt.effect("preserves an unauthorized health error", () => Effect.gen(function* () { - const error = yield* verifyOpenCodeServerVersion( + const error = yield* OpenCodeRuntime.verifyOpenCodeServerVersion( makeHealthClient(() => Promise.reject({ response: { status: 401 }, error: { message: "Unauthorized" } }), ), ).pipe(Effect.flip); - expect(error).toBeInstanceOf(OpenCodeRuntimeError); + expect(error).toBeInstanceOf(OpenCodeRuntime.OpenCodeRuntimeError); expect(error.detail).toContain("status=401"); expect(error.detail).toContain("Unauthorized"); }), @@ -146,7 +142,7 @@ describe("verifyOpenCodeServerVersion", () => { effectIt.effect("aborts a health request when the version check times out", () => Effect.gen(function* () { let requestSignal: AbortSignal | undefined; - const checkFiber = yield* verifyOpenCodeServerVersion( + const checkFiber = yield* OpenCodeRuntime.verifyOpenCodeServerVersion( makeHealthClient((options) => { requestSignal = options?.signal; return new Promise(() => undefined); @@ -213,7 +209,7 @@ server.listen(0, "127.0.0.1", () => { yield* fs.chmod(binaryPath, 0o755); } - const runtime = yield* OpenCodeRuntime; + const runtime = yield* OpenCodeRuntime.OpenCodeRuntime; const server = yield* runtime.startOpenCodeServerProcess({ binaryPath, directory: tempDir, @@ -231,7 +227,7 @@ server.listen(0, "127.0.0.1", () => { }).pipe( Effect.scoped, Effect.provide([ - OpenCodeRuntimeLive.pipe( + OpenCodeRuntime.OpenCodeRuntimeLive.pipe( Layer.provide(OpenCodeServerLedger.layerTest), Layer.provideMerge(NodeServices.layer), ), diff --git a/apps/server/src/provider/opencodeRuntime.inventory.test.ts b/apps/server/src/provider/opencodeRuntime.inventory.test.ts index 96c1ae2c4684..61bc82464049 100644 --- a/apps/server/src/provider/opencodeRuntime.inventory.test.ts +++ b/apps/server/src/provider/opencodeRuntime.inventory.test.ts @@ -15,10 +15,10 @@ import { HostProcessPlatform, } from "@t3tools/shared/hostProcess"; -import { OpenCodeRuntime, OpenCodeRuntimeLive } from "./opencodeRuntime.ts"; +import * as OpenCodeRuntime from "./opencodeRuntime.ts"; import * as OpenCodeServerLedger from "./OpenCodeServerLedger.ts"; -const testLayer = OpenCodeRuntimeLive.pipe( +const testLayer = OpenCodeRuntime.OpenCodeRuntimeLive.pipe( Layer.provide(OpenCodeServerLedger.layerTest), Layer.provideMerge(NodeServices.layer), ); @@ -26,7 +26,7 @@ const testLayer = OpenCodeRuntimeLive.pipe( it.layer(testLayer)("OpenCodeRuntime inventory", (it) => { it.effect("aborts pending SDK requests when inventory loading is interrupted", () => Effect.gen(function* () { - const runtime = yield* OpenCodeRuntime; + const runtime = yield* OpenCodeRuntime.OpenCodeRuntime; const started = yield* Queue.make(); const aborted = yield* Queue.make(); const client = createOpencodeClient({ @@ -65,7 +65,7 @@ it.layer(testLayer)("OpenCodeRuntime inventory", (it) => { it.effect("discovers directory-scoped commands without retaining prompt templates", () => Effect.gen(function* () { - const runtime = yield* OpenCodeRuntime; + const runtime = yield* OpenCodeRuntime.OpenCodeRuntime; const requests: Request[] = []; const client = createOpencodeClient({ baseUrl: "http://opencode.test", @@ -117,7 +117,7 @@ it.layer(testLayer)("OpenCodeRuntime inventory", (it) => { it.effect("keeps provider inventory when agent discovery fails", () => Effect.gen(function* () { - const runtime = yield* OpenCodeRuntime; + const runtime = yield* OpenCodeRuntime.OpenCodeRuntime; const client = { provider: { list: () => @@ -145,7 +145,7 @@ it.layer(testLayer)("OpenCodeRuntime inventory", (it) => { it.effect("keeps provider inventory when skill discovery fails", () => Effect.gen(function* () { - const runtime = yield* OpenCodeRuntime; + const runtime = yield* OpenCodeRuntime.OpenCodeRuntime; const client = { provider: { list: () => @@ -173,7 +173,7 @@ it.layer(testLayer)("OpenCodeRuntime inventory", (it) => { it.effect("keeps only SDK skill metadata in inventory", () => Effect.gen(function* () { - const runtime = yield* OpenCodeRuntime; + const runtime = yield* OpenCodeRuntime.OpenCodeRuntime; const client = { provider: { list: () => @@ -252,7 +252,7 @@ it.layer(testLayer)("OpenCodeRuntime inventory", (it) => { yield* fs.chmod(binaryPath, 0o755); } - const runtime = yield* OpenCodeRuntime; + const runtime = yield* OpenCodeRuntime.OpenCodeRuntime; const inventory = yield* runtime.loadInventoryFromCli({ binaryPath, cwd: tempDir, @@ -270,7 +270,7 @@ it.layer(testLayer)("OpenCodeRuntime inventory", (it) => { it.effect("caps and drains command stdout and stderr when requested", () => Effect.gen(function* () { - const runtime = yield* OpenCodeRuntime; + const runtime = yield* OpenCodeRuntime.OpenCodeRuntime; const executablePath = yield* HostProcessExecutablePath; const outputBytes = 2 * 1024 * 1024; const result = yield* runtime.runOpenCodeCommand({ diff --git a/apps/server/src/provider/providerCompatibility.test.ts b/apps/server/src/provider/providerCompatibility.test.ts index 4fb8f2d74810..bfa69408e77e 100644 --- a/apps/server/src/provider/providerCompatibility.test.ts +++ b/apps/server/src/provider/providerCompatibility.test.ts @@ -11,8 +11,8 @@ import * as Stream from "effect/Stream"; import * as ServerConfig from "../config.ts"; import * as ModelManifest from "./ModelManifest.ts"; import { ProviderRegistryLive } from "./Layers/ProviderRegistry.ts"; -import { ProviderRegistry } from "./Services/ProviderRegistry.ts"; -import { ProviderInstanceRegistry } from "./Services/ProviderInstanceRegistry.ts"; +import * as ProviderRegistry from "./Services/ProviderRegistry.ts"; +import * as ProviderInstanceRegistry from "./Services/ProviderInstanceRegistry.ts"; import type { ProviderInstance } from "./ProviderDriver.ts"; import { makeManualOnlyProviderMaintenanceCapabilities } from "./providerMaintenance.ts"; import { BUILT_IN_DRIVERS } from "./builtInDrivers.ts"; @@ -257,7 +257,7 @@ it.effect("a remote policy refresh preserves a newer health result on the regist forceRefresh: refresh, refreshInBackground: Effect.void, }), - Layer.succeed(ProviderInstanceRegistry, { + Layer.succeed(ProviderInstanceRegistry.ProviderInstanceRegistry, { getInstance: (id) => Effect.succeed(id === instance.instanceId ? instance : undefined), listInstances: Effect.succeed([instance]), listUnavailable: Effect.succeed([]), @@ -269,7 +269,7 @@ it.effect("a remote policy refresh preserves a newer health result on the regist ), ); yield* Effect.gen(function* () { - const registry = yield* ProviderRegistry; + const registry = yield* ProviderRegistry.ProviderRegistry; yield* Deferred.await(started); assert.strictEqual( (yield* registry.getProviders)[0]?.compatibilityAdvisory?.status, diff --git a/apps/server/src/provider/providerInstallation.test.ts b/apps/server/src/provider/providerInstallation.test.ts index 33e33ed450a2..01eaf0367fed 100644 --- a/apps/server/src/provider/providerInstallation.test.ts +++ b/apps/server/src/provider/providerInstallation.test.ts @@ -12,13 +12,13 @@ import * as Layer from "effect/Layer"; import * as Path from "effect/Path"; import * as Stream from "effect/Stream"; -import { layerTest as settingsLayerTest } from "../serverSettings.ts"; -import { CodexInstallation } from "./CodexInstallation.ts"; -import { AntigravityInstallation } from "./AntigravityInstallation.ts"; +import * as ServerSettings from "../serverSettings.ts"; +import * as CodexInstallation from "./CodexInstallation.ts"; +import * as AntigravityInstallation from "./AntigravityInstallation.ts"; import type { ProviderInstance } from "./ProviderDriver.ts"; import { makeProviderInstallation } from "./providerInstallation.ts"; -import { ProviderInstanceRegistry } from "./Services/ProviderInstanceRegistry.ts"; -import { ProviderRegistry } from "./Services/ProviderRegistry.ts"; +import * as ProviderInstanceRegistry from "./Services/ProviderInstanceRegistry.ts"; +import * as ProviderRegistry from "./Services/ProviderRegistry.ts"; const instanceId = ProviderInstanceId.make("antigravity"); const driver = ProviderDriverKind.make("antigravity"); @@ -56,7 +56,7 @@ function instance(kind = driver, id = instanceId): ProviderInstance { const makeHarness = Effect.fn("providerInstallation.test.makeHarness")(function* ( input: { instance?: ProviderInstance; - settings?: Parameters[0]; + settings?: Parameters[0]; } = {}, ) { const calls: string[] = []; @@ -65,20 +65,20 @@ const makeHarness = Effect.fn("providerInstallation.test.makeHarness")(function* const router = yield* makeProviderInstallation().pipe( Effect.provide( Layer.mergeAll( - settingsLayerTest(input.settings), - Layer.mock(ProviderInstanceRegistry)({ + ServerSettings.layerTest(input.settings), + Layer.mock(ProviderInstanceRegistry.ProviderInstanceRegistry)({ getInstance: (id) => Effect.succeed(id === configured.instanceId ? configured : undefined), listInstances: Effect.succeed([configured]), }), - Layer.mock(ProviderRegistry)({ + Layer.mock(ProviderRegistry.ProviderRegistry)({ refreshInstance: () => Effect.sync(() => { calls.push("refresh"); return []; }), }), - Layer.mock(CodexInstallation)({ + Layer.mock(CodexInstallation.CodexInstallation)({ managedDirectory: "/unused-managed-codex", start: Effect.sync(() => { calls.push("codex-start"); @@ -97,7 +97,7 @@ const makeHarness = Effect.fn("providerInstallation.test.makeHarness")(function* calls.push("codex-remove"); }), }), - Layer.mock(AntigravityInstallation)({ + Layer.mock(AntigravityInstallation.AntigravityInstallation)({ managedDirectory: "/unused-managed-runtime", start: Effect.sync(() => { calls.push("start"); diff --git a/apps/server/src/provider/providerInstallation.ts b/apps/server/src/provider/providerInstallation.ts index 5cf99ce27b95..62931e2e0418 100644 --- a/apps/server/src/provider/providerInstallation.ts +++ b/apps/server/src/provider/providerInstallation.ts @@ -12,15 +12,12 @@ import * as Effect from "effect/Effect"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; -import { CodexInstallation, type CodexInstallationError } from "./CodexInstallation.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; -import { - AntigravityInstallation, - type AntigravityInstallationError, -} from "./AntigravityInstallation.ts"; +import * as CodexInstallation from "./CodexInstallation.ts"; +import * as ServerSettings from "../serverSettings.ts"; +import * as AntigravityInstallation from "./AntigravityInstallation.ts"; import { deriveProviderInstanceConfigMap } from "./Layers/ProviderInstanceRegistryHydration.ts"; -import { ProviderInstanceRegistry } from "./Services/ProviderInstanceRegistry.ts"; -import { ProviderRegistry } from "./Services/ProviderRegistry.ts"; +import * as ProviderInstanceRegistry from "./Services/ProviderInstanceRegistry.ts"; +import * as ProviderRegistry from "./Services/ProviderRegistry.ts"; import { mergeProviderInstanceEnvironment } from "./ProviderInstanceEnvironment.ts"; const ANTIGRAVITY = ProviderDriverKind.make("antigravity"); @@ -30,11 +27,11 @@ const decodeAntigravitySettings = Schema.decodeUnknownEffect(AntigravitySettings /** Route instance setup to the environment-owned installer without owning the download. */ export const makeProviderInstallation = Effect.fn("makeProviderInstallation")(function* () { - const antigravityInstallation = yield* AntigravityInstallation; - const codexInstallation = yield* CodexInstallation; - const instances = yield* ProviderInstanceRegistry; - const providers = yield* ProviderRegistry; - const settings = yield* ServerSettingsService; + const antigravityInstallation = yield* AntigravityInstallation.AntigravityInstallation; + const codexInstallation = yield* CodexInstallation.CodexInstallation; + const instances = yield* ProviderInstanceRegistry.ProviderInstanceRegistry; + const providers = yield* ProviderRegistry.ProviderRegistry; + const settings = yield* ServerSettings.ServerSettingsService; const readEntries = Effect.fn("ProviderInstallation.readEntries")(function* ( instanceId: ProviderInstanceId, @@ -102,7 +99,11 @@ export const makeProviderInstallation = Effect.fn("makeProviderInstallation")(fu const failure = (instanceId: ProviderInstanceId) => - (error: AntigravityInstallationError | CodexInstallationError) => + ( + error: + | AntigravityInstallation.AntigravityInstallationError + | CodexInstallation.CodexInstallationError, + ) => new ProviderSetupError({ instanceId, operation: error.operation, detail: error.detail }); const start = Effect.fn("ProviderInstallation.start")(function* (input: ProviderSetupInput) { diff --git a/apps/server/src/provider/providerMaintenanceRunner.test.ts b/apps/server/src/provider/providerMaintenanceRunner.test.ts index 8d059c1fe545..64554ce263d3 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.test.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.test.ts @@ -20,7 +20,7 @@ import { ChildProcessSpawner } from "effect/unstable/process"; import { HostProcessEnvironment, HostProcessPlatform } from "@t3tools/shared/hostProcess"; import { SpawnExecutableResolution } from "@t3tools/shared/shell"; -import { ProviderRegistry, type ProviderRegistryShape } from "./Services/ProviderRegistry.ts"; +import * as ProviderRegistry from "./Services/ProviderRegistry.ts"; import * as ModelManifest from "./ModelManifest.ts"; import * as ProviderMaintenanceRunner from "./providerMaintenanceRunner.ts"; import { @@ -190,7 +190,7 @@ function makeRegistry( ); }); - const registry: ProviderRegistryShape = { + const registry: ProviderRegistry.ProviderRegistryShape = { getProviders: Ref.get(providersRef), refresh: () => Ref.get(providersRef), refreshInstance: () => Ref.get(providersRef), @@ -210,7 +210,7 @@ function makeRegistry( } const makeTestRunner = ( - registry: ProviderRegistryShape, + registry: ProviderRegistry.ProviderRegistryShape, // Generic updater fixtures use synthetic versions. Keep their compatibility // unknown so real harness minimums do not bypass the command under test. manifest: ModelManifest.ModelManifestData = { @@ -228,7 +228,7 @@ const makeTestRunner = ( ProviderMaintenanceRunner.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.succeed(ProviderRegistry, registry), + Layer.succeed(ProviderRegistry.ProviderRegistry, registry), Layer.succeed(ModelManifest.ModelManifest, { current: Effect.succeed(manifest), refresh: Effect.succeed(manifest), diff --git a/apps/server/src/provider/providerMaintenanceRunner.ts b/apps/server/src/provider/providerMaintenanceRunner.ts index 17be8eb087f6..918e9ce3454e 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.ts @@ -23,7 +23,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import * as ModelManifest from "./ModelManifest.ts"; import { resolveProviderCompatibility } from "./providerCompatibility.ts"; -import { ProviderRegistry } from "./Services/ProviderRegistry.ts"; +import * as ProviderRegistry from "./Services/ProviderRegistry.ts"; import { makeProviderMaintenanceCommandCoordinator } from "./providerMaintenanceCommandCoordinator.ts"; import { enrichProviderSnapshotWithVersionAdvisory, @@ -218,7 +218,7 @@ function makeUpdateState(input: { /** @public Service construction is part of the canonical Effect module API. */ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { - const providerRegistry = yield* ProviderRegistry; + const providerRegistry = yield* ProviderRegistry.ProviderRegistry; const manifestService = yield* ModelManifest.ModelManifest; const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; const httpClient = yield* HttpClient.HttpClient; diff --git a/apps/server/src/provider/testUtils/providerRegistryMock.ts b/apps/server/src/provider/testUtils/providerRegistryMock.ts index b2195b1d890c..28b9b0ca4b91 100644 --- a/apps/server/src/provider/testUtils/providerRegistryMock.ts +++ b/apps/server/src/provider/testUtils/providerRegistryMock.ts @@ -1,4 +1,4 @@ -import { ProviderRegistry, type ProviderRegistryShape } from "../Services/ProviderRegistry.ts"; +import * as ProviderRegistry from "../Services/ProviderRegistry.ts"; import type { ServerProvider } from "@t3tools/contracts"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; @@ -7,7 +7,7 @@ import { makeManualOnlyProviderMaintenanceCapabilities } from "../providerMainte const makeProviderRegistryMock = ( providers: ReadonlyArray = [], -): ProviderRegistryShape => ({ +): ProviderRegistry.ProviderRegistryShape => ({ getProviders: Effect.succeed(providers), refresh: () => Effect.succeed(providers), refreshInstance: () => Effect.succeed(providers), @@ -19,4 +19,4 @@ const makeProviderRegistryMock = ( }); export const makeProviderRegistryLayer = (providers: ReadonlyArray = []) => - Layer.succeed(ProviderRegistry, makeProviderRegistryMock(providers)); + Layer.succeed(ProviderRegistry.ProviderRegistry, makeProviderRegistryMock(providers)); diff --git a/apps/server/src/pullRequest/AzureDevOpsPullRequestProvider.ts b/apps/server/src/pullRequest/AzureDevOpsPullRequestProvider.ts index b194e5004734..b11ba6e20563 100644 --- a/apps/server/src/pullRequest/AzureDevOpsPullRequestProvider.ts +++ b/apps/server/src/pullRequest/AzureDevOpsPullRequestProvider.ts @@ -25,7 +25,6 @@ import { type ProviderDiffSlice, type PullRequestProviderApi, } from "./PullRequestProvider.ts"; -import type { AzureDevOpsIterationChanges } from "./AzureDevOpsPullRequestCli.ts"; import type { AzureDevOpsChangeEntry, AzureDevOpsItemContent, @@ -283,7 +282,10 @@ export const make = Effect.gen(function* () { }) => { const latest = input.iterations.at(-1); return latest === undefined - ? Effect.succeed({ changes: [], truncated: false } as AzureDevOpsIterationChanges) + ? Effect.succeed({ + changes: [], + truncated: false, + } as AzureDevOpsPullRequestCli.AzureDevOpsIterationChanges) : cli.listIterationChanges({ cwd: input.cwd, location: input.location, diff --git a/apps/server/src/pullRequest/ForgejoPullRequestProvider.ts b/apps/server/src/pullRequest/ForgejoPullRequestProvider.ts index 73702bc01c3b..73d582c9042c 100644 --- a/apps/server/src/pullRequest/ForgejoPullRequestProvider.ts +++ b/apps/server/src/pullRequest/ForgejoPullRequestProvider.ts @@ -3,7 +3,7 @@ import * as Schema from "effect/Schema"; import * as Result from "effect/Result"; import type { PullRequestCapabilities, PullRequestViewerPermissions } from "@t3tools/contracts"; import { decodeJsonResult } from "@t3tools/shared/schemaJson"; -import { ForgejoCli, type ForgejoApiInput } from "../sourceControl/ForgejoCli.ts"; +import * as ForgejoCli from "../sourceControl/ForgejoCli.ts"; import { parseDiffFileRevisions } from "./bitbucketDiffRevisions.ts"; import { PullRequestProviderError, @@ -64,7 +64,7 @@ const reviewCommentId = (review: typeof ForgejoReview.Type) => /#issuecomment-([1-9]\d*)$/.exec(review.html_url ?? "")?.[1]; export const make = Effect.gen(function* () { - const cli = yield* ForgejoCli; + const cli = yield* ForgejoCli.ForgejoCli; const failure = (operation: string, detail: string, cause?: unknown) => new PullRequestProviderError({ provider: "forgejo", @@ -73,7 +73,7 @@ export const make = Effect.gen(function* () { detail, ...(cause === undefined ? {} : { cause }), }); - const request = (input: ForgejoApiInput) => + const request = (input: ForgejoCli.ForgejoApiInput) => cli.api(input).pipe( Effect.mapError( (error) => @@ -94,7 +94,7 @@ export const make = Effect.gen(function* () { ), ); const read = Effect.fn("ForgejoPullRequestProvider.read")(function* ( - input: ForgejoApiInput, + input: ForgejoCli.ForgejoApiInput, schema: Schema.Codec, ) { const result = yield* request(input); @@ -105,10 +105,12 @@ export const make = Effect.gen(function* () { ? decoded.success : yield* failure(input.path, "Forgejo returned an invalid response.", decoded.failure); }); - const readArray = (input: ForgejoApiInput, schema: Schema.Codec) => - read(input, Schema.NullOr(Schema.Array(schema))).pipe(Effect.map((rows) => rows ?? [])); + const readArray = ( + input: ForgejoCli.ForgejoApiInput, + schema: Schema.Codec, + ) => read(input, Schema.NullOr(Schema.Array(schema))).pipe(Effect.map((rows) => rows ?? [])); const readPage = Effect.fn("ForgejoPullRequestProvider.readPage")(function* ( - input: ForgejoApiInput, + input: ForgejoCli.ForgejoApiInput, schema: Schema.Codec, index: number, ) { @@ -130,7 +132,7 @@ export const make = Effect.gen(function* () { }); // Use a stable, small page size that also works with Forgejo's default maximum of 50. const page = Effect.fn("ForgejoPullRequestProvider.page")(function* ( - input: ForgejoApiInput, + input: ForgejoCli.ForgejoApiInput, schema: Schema.Codec, limit = 500, ) { @@ -142,7 +144,7 @@ export const make = Effect.gen(function* () { } return { items, truncated: true }; }); - const write = (input: ForgejoApiInput) => request(input).pipe(Effect.asVoid); + const write = (input: ForgejoCli.ForgejoApiInput) => request(input).pipe(Effect.asVoid); const getPull = (input: ProviderRepositoryRef & { readonly number: number }) => read( { diff --git a/apps/server/src/pullRequest/PullRequestReadCache.ts b/apps/server/src/pullRequest/PullRequestReadCache.ts index 724fbe490d7f..da48e2694f76 100644 --- a/apps/server/src/pullRequest/PullRequestReadCache.ts +++ b/apps/server/src/pullRequest/PullRequestReadCache.ts @@ -17,7 +17,7 @@ import * as KeyValueStore from "effect/unstable/persistence/KeyValueStore"; import * as Persistable from "effect/unstable/persistence/Persistable"; import * as PersistedCache from "effect/unstable/persistence/PersistedCache"; import * as Persistence from "effect/unstable/persistence/Persistence"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; const CONCURRENT_READS = 512; type ReadError = PullRequestOperationError | PullRequestUnavailableError; @@ -164,7 +164,7 @@ export const make = Effect.gen(function* () { export const layer = Layer.unwrap( Effect.gen(function* () { - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; const path = yield* Path.Path; return Layer.effect(PullRequestReadCache, make).pipe( Layer.provide( diff --git a/apps/server/src/pullRequest/PullRequestService.test.ts b/apps/server/src/pullRequest/PullRequestService.test.ts index 14ade6af58d2..0460eb82f8f2 100644 --- a/apps/server/src/pullRequest/PullRequestService.test.ts +++ b/apps/server/src/pullRequest/PullRequestService.test.ts @@ -25,14 +25,14 @@ import * as PullRequestFilesViewed from "../persistence/PullRequestFilesViewed.t import * as RepositoryIdentityResolver from "../project/RepositoryIdentityResolver.ts"; import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; import * as SourceControlRateLimit from "../sourceControl/SourceControlRateLimit.ts"; -import { ForgejoCli } from "../sourceControl/ForgejoCli.ts"; +import * as ForgejoCli from "../sourceControl/ForgejoCli.ts"; import * as ForgejoPullRequestProvider from "./ForgejoPullRequestProvider.ts"; import { PullRequestProviderError, type ProviderChangeRequest, type PullRequestProviderApi, } from "./PullRequestProvider.ts"; -import { PullRequestProviderRegistry, fromProviders } from "./PullRequestProviderRegistry.ts"; +import * as PullRequestProviderRegistry from "./PullRequestProviderRegistry.ts"; import * as PullRequestService from "./PullRequestService.ts"; import * as PullRequestReadCache from "./PullRequestReadCache.ts"; import { @@ -410,7 +410,10 @@ function makeService(input: { return Effect.flatMap( Layer.build( Layer.mergeAll( - Layer.succeed(PullRequestProviderRegistry, fromProviders(input.providers)), + Layer.succeed( + PullRequestProviderRegistry.PullRequestProviderRegistry, + PullRequestProviderRegistry.fromProviders(input.providers), + ), Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ resolveLink: () => undefined, resolveHandle: @@ -5718,7 +5721,7 @@ it.effect("tracks Forgejo viewed files through its diff and refuses truncated ba let diffReads = 0; const provider = yield* ForgejoPullRequestProvider.make.pipe( Effect.provide( - Layer.mock(ForgejoCli)({ + Layer.mock(ForgejoCli.ForgejoCli)({ api: (input) => { assert.strictEqual(input.host, "forge.example:3000"); const viewer = input.path === "user"; diff --git a/apps/server/src/pullRequest/PullRequestService.ts b/apps/server/src/pullRequest/PullRequestService.ts index 355e57b43612..917821c21353 100644 --- a/apps/server/src/pullRequest/PullRequestService.ts +++ b/apps/server/src/pullRequest/PullRequestService.ts @@ -86,7 +86,7 @@ import { PullRequestProviderError, } from "./PullRequestProvider.ts"; import * as PullRequestReadCache from "./PullRequestReadCache.ts"; -import { PullRequestProviderRegistry } from "./PullRequestProviderRegistry.ts"; +import * as PullRequestProviderRegistry from "./PullRequestProviderRegistry.ts"; import * as ViewedFiles from "./pullRequestViewedFiles.ts"; export interface PullRequestMergeEvent extends PullRequestRef { @@ -628,7 +628,7 @@ const observeRead = Effect.fnUntraced(function* (read: Effect.Effect(64); const pullRequestRefreshes = yield* SubscriptionRef.make(0); - const registry = yield* PullRequestProviderRegistry; + const registry = yield* PullRequestProviderRegistry.PullRequestProviderRegistry; const projects = yield* ProjectService.ProjectService; const repositoryIdentities = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; const sourceControlProviders = yield* SourceControlProviderRegistry.SourceControlProviderRegistry; diff --git a/apps/server/src/relay/AgentAwarenessRelay.test.ts b/apps/server/src/relay/AgentAwarenessRelay.test.ts index ddc6efcabe6e..6a715a7ab23d 100644 --- a/apps/server/src/relay/AgentAwarenessRelay.test.ts +++ b/apps/server/src/relay/AgentAwarenessRelay.test.ts @@ -27,26 +27,18 @@ import * as Stream from "effect/Stream"; import * as TestClock from "effect/testing/TestClock"; import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient"; -import { SecretStoreReadError, ServerSecretStore } from "../auth/ServerSecretStore.ts"; +import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; import { PUBLISH_AGENT_ACTIVITY_SECRET, RELAY_ENVIRONMENT_CREDENTIAL_SECRET, RELAY_ISSUER_SECRET, RELAY_URL_SECRET, } from "../cloud/config.ts"; -import { ServerEnvironment } from "../environment/ServerEnvironment.ts"; -import { - layerMemory as projectionStoreMemoryLayer, - ProjectionStoreV2, -} from "../orchestration-v2/ProjectionStore.ts"; -import { ThreadManagementService } from "../orchestration-v2/ThreadManagementService.ts"; -import { ProjectService } from "../project/ProjectService.ts"; -import { - make, - makeAgentAwarenessPublishWorker, - resolveAgentAwarenessRelayActiveThreadIds, - shouldPublishAgentAwarenessEvent, -} from "./AgentAwarenessRelay.ts"; +import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; +import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; +import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; +import * as ProjectService from "../project/ProjectService.ts"; +import * as AgentAwarenessRelay from "./AgentAwarenessRelay.ts"; const THREAD_ID = ThreadId.make("relay-thread"); const SECOND_THREAD_ID = ThreadId.make("relay-thread-2"); @@ -109,7 +101,7 @@ describe("startup agent activity", () => { const newCompleted = ThreadId.make("new-completed"); const oldFailed = ThreadId.make("old-failed"); const newFailed = ThreadId.make("new-failed"); - const ids = resolveAgentAwarenessRelayActiveThreadIds({ + const ids = AgentAwarenessRelay.resolveAgentAwarenessRelayActiveThreadIds({ environmentId: EnvironmentId.make("relay-env"), startedAt, projects: [{ id: PROJECT_ID, title: "Project" }], @@ -165,13 +157,16 @@ const makeTestRelay = Effect.fnUntraced(function* ( ], ); const secretReads: string[] = []; - const secrets = ServerSecretStore.of({ + const secrets = ServerSecretStore.ServerSecretStore.of({ get: (name) => Effect.suspend(() => { secretReads.push(name); if (options.failSecretRead?.(name)) { return Effect.fail( - new SecretStoreReadError({ resource: name, cause: "temporary read failure" }), + new ServerSecretStore.SecretStoreReadError({ + resource: name, + cause: "temporary read failure", + }), ); } return Effect.succeed(Option.fromUndefinedOr(values.get(name))); @@ -185,7 +180,7 @@ const makeTestRelay = Effect.fnUntraced(function* ( const shellReads: ThreadId[] = []; // Catch-up publishes read the whole shell once each. const catchUp = { shellSnapshotReads: 0 }; - const threads = ThreadManagementService.of({ + const threads = ThreadManagementService.ThreadManagementService.of({ getThreadShell: (threadId) => Effect.sync(() => shellReads.push(threadId)).pipe( Effect.andThen(options.readShell?.(threadId) ?? Ref.get(currentShell)), @@ -243,14 +238,14 @@ const makeTestRelay = Effect.fnUntraced(function* ( }, { preconnect: () => {} }, ); - const relay = yield* make.pipe( - Effect.provideService(ServerSecretStore, secrets), - Effect.provideService(ThreadManagementService, threads), - Effect.provideService(ServerEnvironment, { + const relay = yield* AgentAwarenessRelay.make.pipe( + Effect.provideService(ServerSecretStore.ServerSecretStore, secrets), + Effect.provideService(ThreadManagementService.ThreadManagementService, threads), + Effect.provideService(ServerEnvironment.ServerEnvironment, { getEnvironmentId: Effect.succeed(EnvironmentId.make("relay-environment")), getDescriptor: unused(), }), - Effect.provideService(ProjectService, { + Effect.provideService(ProjectService.ProjectService, { create: unused, bootstrap: unused, update: unused, @@ -288,7 +283,7 @@ describe("AgentAwarenessRelay", () => { "thread.visited", "thread.pinned", ] as const) { - assert.isFalse(shouldPublishAgentAwarenessEvent({ type })); + assert.isFalse(AgentAwarenessRelay.shouldPublishAgentAwarenessEvent({ type })); } for (const type of [ "run.created", @@ -304,18 +299,20 @@ describe("AgentAwarenessRelay", () => { "thread.unarchived", "thread.deleted", ] as const) { - assert.isTrue(shouldPublishAgentAwarenessEvent({ type })); + assert.isTrue(AgentAwarenessRelay.shouldPublishAgentAwarenessEvent({ type })); } }); it("does not publish imported thread creation as new agent activity", () => { assert.isFalse( - shouldPublishAgentAwarenessEvent({ + AgentAwarenessRelay.shouldPublishAgentAwarenessEvent({ type: "thread.created", payload: { historyOrigin: "v1_import" }, }), ); - assert.isTrue(shouldPublishAgentAwarenessEvent({ type: "thread.created", payload: {} })); + assert.isTrue( + AgentAwarenessRelay.shouldPublishAgentAwarenessEvent({ type: "thread.created", payload: {} }), + ); }); it.effect("coalesces queued updates and reruns a thread dirtied during publishing", () => @@ -326,7 +323,7 @@ describe("AgentAwarenessRelay", () => { const releaseRerun = yield* Deferred.make(); const processed: Array<{ threadId: ThreadId; revision: number }> = []; let revision = 1; - const worker = yield* makeAgentAwarenessPublishWorker((threadId) => + const worker = yield* AgentAwarenessRelay.makeAgentAwarenessPublishWorker((threadId) => Effect.gen(function* () { const currentRevision = revision; const index = processed.length; @@ -687,7 +684,7 @@ describe("AgentAwarenessRelay", () => { ); it.effect("publishes a held completion when its background item ends, not on tool output", () => Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const events = yield* Queue.unbounded(); // The relay pulls the next event only after it has handled the previous one. const pulls = yield* Queue.unbounded(); @@ -843,12 +840,12 @@ describe("AgentAwarenessRelay", () => { publications.map((publication) => publication.state?.phase), ["running", "completed"], ); - }).pipe(Effect.scoped, Effect.provide(projectionStoreMemoryLayer)), + }).pipe(Effect.scoped, Effect.provide(ProjectionStore.layerMemory)), ); }); describe.sequential("startup catch-up", () => { - const link = (secrets: ServerSecretStore["Service"]) => + const link = (secrets: ServerSecretStore.ServerSecretStore["Service"]) => Effect.all( [ secrets.set(RELAY_URL_SECRET, new TextEncoder().encode("https://relay.example.test")), @@ -857,7 +854,7 @@ describe.sequential("startup catch-up", () => { ], { discard: true }, ); - const enablePublishing = (secrets: ServerSecretStore["Service"]) => + const enablePublishing = (secrets: ServerSecretStore.ServerSecretStore["Service"]) => secrets.set(PUBLISH_AGENT_ACTIVITY_SECRET, new TextEncoder().encode("true")); const linkChecks = (secretReads: ReadonlyArray) => secretReads.filter((name) => name === RELAY_URL_SECRET).length; diff --git a/apps/server/src/resourceTelemetry/DesktopTelemetryReceiver.ts b/apps/server/src/resourceTelemetry/DesktopTelemetryReceiver.ts index 67842addd7df..2084fd47350a 100644 --- a/apps/server/src/resourceTelemetry/DesktopTelemetryReceiver.ts +++ b/apps/server/src/resourceTelemetry/DesktopTelemetryReceiver.ts @@ -25,8 +25,8 @@ import * as Scope from "effect/Scope"; import * as Stream from "effect/Stream"; import * as Ndjson from "effect/unstable/encoding/Ndjson"; -import { ServerConfig } from "../config.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; +import * as ServerConfig from "../config.ts"; +import * as ServerSettings from "../serverSettings.ts"; import { subscribeBeforeSnapshotWithoutMutex } from "../utils/subscribeBeforeSnapshot.ts"; const INITIAL_SAMPLE_DEADLINE_MS = 90_000; @@ -329,8 +329,8 @@ export function requireDesktopTelemetryWriteProgress( /** @public Service construction is part of the canonical Effect module API. */ export const make = Effect.fn("resourceTelemetry.desktopTelemetryReceiver.make")(function* () { - const config = yield* ServerConfig; - const serverSettings = yield* ServerSettingsService; + const config = yield* ServerConfig.ServerConfig; + const serverSettings = yield* ServerSettings.ServerSettingsService; const latest = yield* Ref.make(Option.none()); const receiverStartedAt = yield* DateTime.now; const lastContactAtMs = yield* Ref.make( diff --git a/apps/server/src/resourceTelemetry/NativeTelemetryClient.ts b/apps/server/src/resourceTelemetry/NativeTelemetryClient.ts index 40254bfe80aa..cf618aedbea7 100644 --- a/apps/server/src/resourceTelemetry/NativeTelemetryClient.ts +++ b/apps/server/src/resourceTelemetry/NativeTelemetryClient.ts @@ -36,7 +36,7 @@ import * as Ndjson from "effect/unstable/encoding/Ndjson"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import * as ResourceMonitorBinary from "./ResourceMonitorBinary.ts"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import { subscribeBeforeSnapshotWithoutMutex } from "../utils/subscribeBeforeSnapshot.ts"; const SAMPLE_INTERVAL_MS = 1_000; @@ -365,7 +365,7 @@ export const make = Effect.fn("resourceTelemetry.nativeTelemetryClient.make")(fu const binary = yield* ResourceMonitorBinary.ResourceMonitorBinary; const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; const crypto = yield* Crypto.Crypto; - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; const initializedAt = yield* DateTime.now; const state = yield* Ref.make(initialState); const collectionControl = yield* Ref.make({ diff --git a/apps/server/src/resourceTelemetry/ResourceMonitorBinary.test.ts b/apps/server/src/resourceTelemetry/ResourceMonitorBinary.test.ts index 7fa23aac3908..7032691f0dd0 100644 --- a/apps/server/src/resourceTelemetry/ResourceMonitorBinary.test.ts +++ b/apps/server/src/resourceTelemetry/ResourceMonitorBinary.test.ts @@ -8,7 +8,7 @@ import { afterEach, assert, describe, expect, it, vi } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import * as ResourceMonitorBinary from "./ResourceMonitorBinary.ts"; // The override checks POSIX exec bits on a real file under a linux platform diff --git a/apps/server/src/resourceTelemetry/ResourceMonitorBinary.ts b/apps/server/src/resourceTelemetry/ResourceMonitorBinary.ts index 52c123fe6625..c4b9d6a85149 100644 --- a/apps/server/src/resourceTelemetry/ResourceMonitorBinary.ts +++ b/apps/server/src/resourceTelemetry/ResourceMonitorBinary.ts @@ -11,7 +11,7 @@ import * as Option from "effect/Option"; import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; export class ResourceMonitorBinaryUnsupported extends Schema.TaggedError()( "ResourceMonitorBinaryUnsupported", @@ -136,7 +136,7 @@ function resourceMonitorRustTarget( } export const make = Effect.fn("resourceTelemetry.resourceMonitorBinary.make")(function* () { - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const platform = yield* HostProcessPlatform; diff --git a/apps/server/src/review/ReviewService.test.ts b/apps/server/src/review/ReviewService.test.ts index 01a4692264e0..736a34d2c446 100644 --- a/apps/server/src/review/ReviewService.test.ts +++ b/apps/server/src/review/ReviewService.test.ts @@ -5,7 +5,7 @@ import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; import * as PlatformError from "effect/PlatformError"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import * as GitVcsDriver from "../vcs/GitVcsDriver.ts"; import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; import * as ReviewService from "./ReviewService.ts"; diff --git a/apps/server/src/scheduledTasks/ScheduledTaskService.schedule.test.ts b/apps/server/src/scheduledTasks/ScheduledTaskService.schedule.test.ts index ae5abcc86f7c..c4cd4a816433 100644 --- a/apps/server/src/scheduledTasks/ScheduledTaskService.schedule.test.ts +++ b/apps/server/src/scheduledTasks/ScheduledTaskService.schedule.test.ts @@ -8,8 +8,8 @@ import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; import * as TestClock from "effect/testing/TestClock"; -import { ThreadLaunchService } from "../orchestration-v2/ThreadLaunchService.ts"; -import { ThreadManagementService } from "../orchestration-v2/ThreadManagementService.ts"; +import * as ThreadLaunchService from "../orchestration-v2/ThreadLaunchService.ts"; +import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; import * as ScheduledTaskService from "./ScheduledTaskService.ts"; @@ -20,8 +20,8 @@ it.effect("rejects a stale form save after deletion while preserving explicit-id const dependencies = Layer.mergeAll( NodeCrypto.layer, Scheduler.layer, - Layer.mock(ThreadLaunchService)({}), - Layer.mock(ThreadManagementService)({}), + Layer.mock(ThreadLaunchService.ThreadLaunchService)({}), + Layer.mock(ThreadManagementService.ThreadManagementService)({}), ); yield* Effect.gen(function* () { const service = yield* ScheduledTaskService.ScheduledTaskService; @@ -62,8 +62,8 @@ it.effect("preserves a due run when a save only pads the scheduled hour", () => const dependencies = Layer.mergeAll( NodeCrypto.layer, Scheduler.layer, - Layer.mock(ThreadLaunchService)({}), - Layer.mock(ThreadManagementService)({}), + Layer.mock(ThreadLaunchService.ThreadLaunchService)({}), + Layer.mock(ThreadManagementService.ThreadManagementService)({}), ); yield* Effect.gen(function* () { const service = yield* ScheduledTaskService.ScheduledTaskService; diff --git a/apps/server/src/scheduledTasks/ScheduledTaskService.test.ts b/apps/server/src/scheduledTasks/ScheduledTaskService.test.ts index d571013a1605..19bb8bf2fa0e 100644 --- a/apps/server/src/scheduledTasks/ScheduledTaskService.test.ts +++ b/apps/server/src/scheduledTasks/ScheduledTaskService.test.ts @@ -18,7 +18,7 @@ import * as TestClock from "effect/testing/TestClock"; import * as ThreadLaunchService from "../orchestration-v2/ThreadLaunchService.ts"; import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; -import { layer as scheduledTaskServiceLayer, listDueTasks } from "./ScheduledTaskService.ts"; +import * as ScheduledTaskService from "./ScheduledTaskService.ts"; const isScheduledTaskError = Schema.is(ScheduledTaskError); @@ -113,7 +113,7 @@ it.effect("loads only due tasks and skips corrupt due rows without decoding sett yield* insertRow(sql, { ...row, prompt: secret }, now); } const warnings: unknown[] = []; - const tasks = yield* listDueTasks(DateTime.makeUnsafe(now)).pipe( + const tasks = yield* ScheduledTaskService.listDueTasks(DateTime.makeUnsafe(now)).pipe( Effect.provide( Logger.layer([ Logger.make(({ message }) => { @@ -195,7 +195,7 @@ it.effect( Effect.gen(function* () { yield* Layer.build( Layer.provideMerge( - scheduledTaskServiceLayer, + ScheduledTaskService.layer, Layer.mergeAll( Layer.mock(ThreadLaunchService.ThreadLaunchService)({ launch: () => @@ -292,7 +292,7 @@ it.effect( Effect.gen(function* () { yield* Layer.build( Layer.provideMerge( - scheduledTaskServiceLayer, + ScheduledTaskService.layer, Layer.mergeAll( Layer.mock(ThreadLaunchService.ThreadLaunchService)({ launch: () => diff --git a/apps/server/src/scheduling/Scheduler.integration.test.ts b/apps/server/src/scheduling/Scheduler.integration.test.ts index 0f88bfe0c43c..3a437ebe533c 100644 --- a/apps/server/src/scheduling/Scheduler.integration.test.ts +++ b/apps/server/src/scheduling/Scheduler.integration.test.ts @@ -18,13 +18,13 @@ import * as Ref from "effect/Ref"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as TestClock from "effect/testing/TestClock"; -import { ProjectionStoreV2 } from "../orchestration-v2/ProjectionStore.ts"; -import { ThreadLaunchService } from "../orchestration-v2/ThreadLaunchService.ts"; -import { ThreadManagementService } from "../orchestration-v2/ThreadManagementService.ts"; -import { workerLive as recoveryWorker } from "../orchestration-v2/UsageLimitRecoveryWorker.ts"; +import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; +import * as ThreadLaunchService from "../orchestration-v2/ThreadLaunchService.ts"; +import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; +import * as UsageLimitRecoveryWorker from "../orchestration-v2/UsageLimitRecoveryWorker.ts"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; import * as ScheduledTasks from "../scheduledTasks/ScheduledTaskService.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; +import * as ServerSettings from "../serverSettings.ts"; import * as Scheduler from "./Scheduler.ts"; it.effect.each(["on time", "after restart"])( @@ -87,13 +87,13 @@ it.effect.each(["on time", "after restart"])( const receipts = yield* Queue.unbounded<"task" | "retry">(); const dependencies = Layer.mergeAll( NodeCrypto.layer, - Layer.mock(ThreadLaunchService)({ + Layer.mock(ThreadLaunchService.ThreadLaunchService)({ launch: () => Queue.offer(receipts, "task").pipe( Effect.andThen(Effect.die("fixture dispatch failure")), ), }), - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ dispatch: (command) => Ref.update(commands, (all) => [...all, command]).pipe( Effect.andThen( @@ -103,18 +103,20 @@ it.effect.each(["on time", "after restart"])( Effect.as({ sequence: 1, storedEvents: [] }), ), }), - Layer.mock(ProjectionStoreV2)({ + Layer.mock(ProjectionStore.ProjectionStoreV2)({ getLimitRecoveryCandidates: () => Ref.get(current).pipe( Effect.map((shell) => (shell.status === "failed" ? [shell] : [])), ), }), - Layer.mock(ServerSettingsService)({ getSettings: Effect.succeed(DEFAULT_SERVER_SETTINGS) }), - ); - const workers = Layer.mergeAll(ScheduledTasks.layer, recoveryWorker).pipe( - Layer.provide(dependencies), - Layer.provide(Scheduler.layer), + Layer.mock(ServerSettings.ServerSettingsService)({ + getSettings: Effect.succeed(DEFAULT_SERVER_SETTINGS), + }), ); + const workers = Layer.mergeAll( + ScheduledTasks.layer, + UsageLimitRecoveryWorker.workerLive, + ).pipe(Layer.provide(dependencies), Layer.provide(Scheduler.layer)); yield* Effect.gen(function* () { const tasks = yield* ScheduledTasks.ScheduledTaskService; const { task } = yield* tasks.upsert({ diff --git a/apps/server/src/server.ts b/apps/server/src/server.ts index d3afebcc3611..98039e56642a 100644 --- a/apps/server/src/server.ts +++ b/apps/server/src/server.ts @@ -41,7 +41,7 @@ import * as NodePtyAdapter from "./terminal/NodePtyAdapter.ts"; import { pullRequestHttpApiLayer } from "./pullRequest/http.ts"; import * as PullRequestProviderRegistry from "./pullRequest/PullRequestProviderRegistry.ts"; import * as PullRequestService from "./pullRequest/PullRequestService.ts"; -import { layerConfig as SqlitePersistenceLayerLive } from "./persistence/Layers/Sqlite.ts"; +import * as SqlitePersistence from "./persistence/Layers/Sqlite.ts"; import * as PullRequestFilesViewed from "./persistence/PullRequestFilesViewed.ts"; import * as ServerLifecycleEvents from "./serverLifecycleEvents.ts"; import * as AnalyticsService from "./telemetry/AnalyticsService.ts"; @@ -80,11 +80,11 @@ import { ProviderRegistryLive } from "./provider/Layers/ProviderRegistry.ts"; import * as ServerSettings from "./serverSettings.ts"; import * as ProjectEnrichmentService from "./project/ProjectEnrichmentService.ts"; import * as NativeAppIconResolver from "./assets/NativeAppIconResolver.ts"; -import { AntigravityInstallation } from "./provider/AntigravityInstallation.ts"; -import { CodexInstallation } from "./provider/CodexInstallation.ts"; -import { ProviderInstanceRegistry } from "./provider/Services/ProviderInstanceRegistry.ts"; -import { layerFromProviderInstanceRegistry as providerAdapterRegistryLayerFromProviderInstances } from "./orchestration-v2/ProviderAdapterRegistry.ts"; -import { ProviderRegistry } from "./provider/Services/ProviderRegistry.ts"; +import * as AntigravityInstallation from "./provider/AntigravityInstallation.ts"; +import * as CodexInstallation from "./provider/CodexInstallation.ts"; +import * as ProviderInstanceRegistry from "./provider/Services/ProviderInstanceRegistry.ts"; +import * as ProviderAdapterRegistry from "./orchestration-v2/ProviderAdapterRegistry.ts"; +import * as ProviderRegistry from "./provider/Services/ProviderRegistry.ts"; import { ProviderUsageLimitsIngestionLive } from "./provider/Layers/ProviderUsageLimitsIngestion.ts"; import * as UsageLimitSources from "./usage/UsageLimitSources.ts"; import * as ProjectFaviconResolver from "./project/ProjectFaviconResolver.ts"; @@ -172,7 +172,7 @@ import { projectHttpApiLayer } from "./project/http.ts"; import * as NetService from "@t3tools/shared/Net"; import * as RelayClient from "@t3tools/shared/relayClient"; import { disableTailscaleServe, ensureTailscaleServe } from "@t3tools/tailscale"; -import { forkParked, ServerActivation } from "./serverActivation.ts"; +import * as ServerActivation from "./serverActivation.ts"; // MCP handoff thread IDs include escaped provenance and can exceed find-my-way's // 100-character default for one path segment. @@ -195,7 +195,7 @@ const PtyAdapterLive = NodePtyAdapter.layer; const ServerSettingsLayerLive = ServerSettings.layer.pipe( Layer.provide(ServerSecretStore.layer), - Layer.provideMerge(SqlitePersistenceLayerLive), + Layer.provideMerge(SqlitePersistence.layerConfig), ); const NativeTelemetryLayerLive = NativeTelemetryClient.layer.pipe( @@ -261,7 +261,7 @@ const HttpServerLive = Layer.unwrap( const PlatformServicesLive = NodeServices.layer; -const PersistenceLayerLive = Layer.empty.pipe(Layer.provideMerge(SqlitePersistenceLayerLive)); +const PersistenceLayerLive = Layer.empty.pipe(Layer.provideMerge(SqlitePersistence.layerConfig)); const VcsDriverRegistryLayerLive = VcsDriverRegistry.layer.pipe( Layer.provide(VcsProjectConfig.layer), @@ -474,10 +474,10 @@ const ThreadPullRequestWorkerLive = Layer.effectDiscard( const ProviderInstallationRefreshLive = Layer.effectDiscard( Effect.gen(function* () { - const antigravity = yield* AntigravityInstallation; - const codex = yield* CodexInstallation; - const instances = yield* ProviderInstanceRegistry; - const providers = yield* ProviderRegistry; + const antigravity = yield* AntigravityInstallation.AntigravityInstallation; + const codex = yield* CodexInstallation.CodexInstallation; + const instances = yield* ProviderInstanceRegistry.ProviderInstanceRegistry; + const providers = yield* ProviderRegistry.ProviderRegistry; yield* Stream.merge( antigravity.changes.pipe( Stream.changesWith((a, b) => a.installedVersion === b.installedVersion), @@ -550,7 +550,12 @@ const RuntimeCoreDependenciesBaseLive = Layer.mergeAll( // `providerInstances` hydration merges `settings.providers.` // with explicit `providerInstances` entries on boot. Layer.provideMerge(ProviderInstanceRegistryHydrationLive), - Layer.provideMerge(Layer.mergeAll(AntigravityInstallation.layer, CodexInstallation.layer)), + Layer.provideMerge( + Layer.mergeAll( + AntigravityInstallation.AntigravityInstallation.layer, + CodexInstallation.CodexInstallation.layer, + ), + ), ); const RuntimeCoreDependenciesLive = RuntimeCoreDependenciesBaseLive.pipe( @@ -639,7 +644,9 @@ const makeRoutesLayer = Layer.mergeAll( // delegation targets through the same live adapter facade the V2 // orchestrator uses, so MCP capability reporting can never drift from // what dispatch can actually serve. - McpHttpServer.layer.pipe(Layer.provide(providerAdapterRegistryLayerFromProviderInstances)), + McpHttpServer.layer.pipe( + Layer.provide(ProviderAdapterRegistry.layerFromProviderInstanceRegistry), + ), // Last, so no route layer can replace the server's one TracerDisabledWhen. untracedRequestsLayer, ).pipe( @@ -658,7 +665,7 @@ const makeServerLayer = Layer.unwrap( const config = yield* ServerConfig.ServerConfig; const activation = yield* Deferred.make(); const awaitActivation = Deferred.await(activation); - const activationLayer = Layer.succeed(ServerActivation, awaitActivation); + const activationLayer = Layer.succeed(ServerActivation.ServerActivation, awaitActivation); const runtimeStateParked = yield* Deferred.make(); const tailscaleParked = yield* Deferred.make(); const cloudLinkParked = yield* Deferred.make(); @@ -786,7 +793,7 @@ const makeServerLayer = Layer.unwrap( if (cleanupBeforeActivation) { yield* Effect.addFinalizer(() => releaseManagedTunnel); } - yield* forkParked( + yield* ServerActivation.forkParked( Effect.gen(function* () { if (!cleanupBeforeActivation) { yield* Effect.addFinalizer(() => releaseManagedTunnel); diff --git a/apps/server/src/serverActivation.test.ts b/apps/server/src/serverActivation.test.ts index a4f942a95b5d..dc41b884ab36 100644 --- a/apps/server/src/serverActivation.test.ts +++ b/apps/server/src/serverActivation.test.ts @@ -2,7 +2,7 @@ import { expect, it } from "@effect/vitest"; import * as Deferred from "effect/Deferred"; import * as Effect from "effect/Effect"; -import { forkParked, ServerActivation } from "./serverActivation.ts"; +import * as ServerActivation from "./serverActivation.ts"; it.effect("proves a root is parked before returning and releases it with one gate", () => Effect.scoped( @@ -10,8 +10,8 @@ it.effect("proves a root is parked before returning and releases it with one gat const activation = yield* Deferred.make(); const ran = yield* Deferred.make(); - yield* forkParked(Deferred.succeed(ran, undefined)).pipe( - Effect.provideService(ServerActivation, Deferred.await(activation)), + yield* ServerActivation.forkParked(Deferred.succeed(ran, undefined)).pipe( + Effect.provideService(ServerActivation.ServerActivation, Deferred.await(activation)), ); expect(yield* Deferred.isDone(ran)).toBe(false); diff --git a/apps/server/src/serverLogger.ts b/apps/server/src/serverLogger.ts index 6d19907c20ca..795a6975312e 100644 --- a/apps/server/src/serverLogger.ts +++ b/apps/server/src/serverLogger.ts @@ -6,10 +6,10 @@ import * as References from "effect/References"; import * as OtlpExporter from "effect/unstable/observability/OtlpExporter"; import * as OtlpLogger from "effect/unstable/observability/OtlpLogger"; -import { otlpResource, ServerConfig } from "./config.ts"; +import * as ServerConfig from "./config.ts"; export const ServerLoggerLive = Effect.gen(function* () { - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; const minimumLogLevelLayer = Layer.succeed(References.MinimumLogLevel, config.logLevel); const logs = config.otlpLogsExport; @@ -20,7 +20,7 @@ export const ServerLoggerLive = Effect.gen(function* () { url: config.otlpLogsUrl, exportInterval: `${logs.exportIntervalMs} millis`, headers: logs.headers, - resource: otlpResource(config), + resource: ServerConfig.otlpResource(config), }); // `Logger.layer` writes the whole logger set rather than adding to it, so diff --git a/apps/server/src/sourceControl/PrTemplateDetection.test.ts b/apps/server/src/sourceControl/PrTemplateDetection.test.ts index 5b91e631112f..1c2721e8661b 100644 --- a/apps/server/src/sourceControl/PrTemplateDetection.test.ts +++ b/apps/server/src/sourceControl/PrTemplateDetection.test.ts @@ -6,7 +6,7 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Path from "effect/Path"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import * as GitVcsDriver from "../vcs/GitVcsDriver.ts"; import * as VcsProcess from "../vcs/VcsProcess.ts"; import { detectPrTemplate } from "./PrTemplateDetection.ts"; diff --git a/apps/server/src/sourceControl/SourceControlDiscovery.ts b/apps/server/src/sourceControl/SourceControlDiscovery.ts index 2628360780e1..e178148f131f 100644 --- a/apps/server/src/sourceControl/SourceControlDiscovery.ts +++ b/apps/server/src/sourceControl/SourceControlDiscovery.ts @@ -8,7 +8,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import * as VcsProcess from "../vcs/VcsProcess.ts"; import { detailFromCause, firstNonEmptyLine } from "./SourceControlProviderDiscovery.ts"; import * as SourceControlProviderRegistry from "./SourceControlProviderRegistry.ts"; @@ -66,7 +66,7 @@ export class SourceControlDiscovery extends Context.Service< /** @public Service construction is part of the canonical Effect module API. */ export const make = Effect.gen(function* () { - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; const process = yield* VcsProcess.VcsProcess; const sourceControlProviders = yield* SourceControlProviderRegistry.SourceControlProviderRegistry; diff --git a/apps/server/src/sourceControl/SourceControlProviderRegistry.ts b/apps/server/src/sourceControl/SourceControlProviderRegistry.ts index 422b2c991040..a497c852bcd6 100644 --- a/apps/server/src/sourceControl/SourceControlProviderRegistry.ts +++ b/apps/server/src/sourceControl/SourceControlProviderRegistry.ts @@ -22,7 +22,7 @@ import { refineUnknownRemoteProvider, type SourceControlProviderDiscoverySpec, } from "./SourceControlProviderDiscovery.ts"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; import * as VcsProcess from "../vcs/VcsProcess.ts"; @@ -200,7 +200,7 @@ function bindProviderContext( /** @public Service construction is part of the canonical Effect module API. */ export const makeWithProviders = Effect.fn("makeSourceControlProviderRegistryWithProviders")( function* (registrations: ReadonlyArray) { - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; const process = yield* VcsProcess.VcsProcess; const vcsRegistry = yield* VcsDriverRegistry.VcsDriverRegistry; const providers = new Map< diff --git a/apps/server/src/sourceControl/SourceControlRepositoryService.ts b/apps/server/src/sourceControl/SourceControlRepositoryService.ts index 9bb3b1e029b6..eaf65b5cd662 100644 --- a/apps/server/src/sourceControl/SourceControlRepositoryService.ts +++ b/apps/server/src/sourceControl/SourceControlRepositoryService.ts @@ -19,7 +19,7 @@ import { type SourceControlRepositoryLookupInput, } from "@t3tools/contracts"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import { expandHomePathWith } from "../pathExpansion.ts"; import { parseGitCloneProgressLine, @@ -155,7 +155,7 @@ function selectRemoteUrl( /** @public Service construction is part of the canonical Effect module API. */ export const make = Effect.gen(function* () { - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; const fileSystem = yield* FileSystem.FileSystem; const git = yield* GitVcsDriver.GitVcsDriver; const path = yield* Path.Path; diff --git a/apps/server/src/startupAccess.ts b/apps/server/src/startupAccess.ts index 7df131669bab..041d43315ee8 100644 --- a/apps/server/src/startupAccess.ts +++ b/apps/server/src/startupAccess.ts @@ -4,7 +4,7 @@ import { QrCode } from "@t3tools/shared/qrCode"; import * as Effect from "effect/Effect"; import { HttpServer } from "effect/unstable/http"; -import { ServerConfig } from "./config.ts"; +import * as ServerConfig from "./config.ts"; import * as EnvironmentAuth from "./auth/EnvironmentAuth.ts"; export interface HeadlessServeAccessInfo { @@ -131,7 +131,7 @@ export const formatHeadlessServeOutput = (accessInfo: HeadlessServeAccessInfo): ].join("\n"); export const issueHeadlessServeAccessInfo = Effect.fn("issueHeadlessServeAccessInfo")(function* () { - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const httpServer = yield* HttpServer.HttpServer; const serverAuth = yield* EnvironmentAuth.EnvironmentAuth; const connectionString = resolveHeadlessConnectionString( diff --git a/apps/server/src/storageCleanup.ts b/apps/server/src/storageCleanup.ts index e4a5bf34d0fa..960e2459fb30 100644 --- a/apps/server/src/storageCleanup.ts +++ b/apps/server/src/storageCleanup.ts @@ -29,8 +29,8 @@ import * as Stream from "effect/Stream"; import * as ServerConfig from "./config.ts"; import * as GitManager from "./git/GitManager.ts"; import * as ProjectStore from "./orchestration-v2/ProjectStore.ts"; -import { OrchestratorV2 } from "./orchestration-v2/Orchestrator.ts"; -import { ProjectionStoreV2 } from "./orchestration-v2/ProjectionStore.ts"; +import * as Orchestrator from "./orchestration-v2/Orchestrator.ts"; +import * as ProjectionStore from "./orchestration-v2/ProjectionStore.ts"; import { threadHasQueuedTurnStart } from "./orchestration-v2/ThreadSettlementService.ts"; import { forkParked } from "./serverActivation.ts"; import * as Settings from "./serverSettings.ts"; @@ -109,8 +109,8 @@ export const make = Effect.gen(function* () { const config = yield* ServerConfig.ServerConfig; const settingsService = yield* Settings.ServerSettingsService; const projectStore = yield* ProjectStore.ProjectStoreV2; - const engine = yield* OrchestratorV2; - const projections = yield* ProjectionStoreV2; + const engine = yield* Orchestrator.OrchestratorV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const sql = yield* SqlClient.SqlClient; const git = yield* GitVcsDriver.GitVcsDriver; const gitManager = yield* GitManager.GitManager; diff --git a/apps/server/src/usage/UsageLimitSources.ts b/apps/server/src/usage/UsageLimitSources.ts index 957a6ab3321e..da0138ae2f55 100644 --- a/apps/server/src/usage/UsageLimitSources.ts +++ b/apps/server/src/usage/UsageLimitSources.ts @@ -34,7 +34,7 @@ import * as Semaphore from "effect/Semaphore"; import * as Stream from "effect/Stream"; import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; +import * as Settings from "../serverSettings.ts"; import { makeCliproxyApi } from "./cliproxyApi.ts"; export class UsageLimitSources extends Context.Service< @@ -63,7 +63,7 @@ function sourceLabel(id: string, config: UsageLimitSourceConfig): string { /** @public Service construction is part of the canonical Effect module API. */ export const make = Effect.gen(function* () { const api = yield* makeCliproxyApi; - const settingsService = yield* ServerSettingsService; + const settingsService = yield* Settings.ServerSettingsService; const backgroundPolicy = yield* BackgroundPolicy.BackgroundPolicy; const stateRef = yield* Ref.make>([]); const changes = yield* Effect.acquireRelease( diff --git a/apps/server/src/usage/UsageService.ts b/apps/server/src/usage/UsageService.ts index ab116d560ed2..a3b07d64d71b 100644 --- a/apps/server/src/usage/UsageService.ts +++ b/apps/server/src/usage/UsageService.ts @@ -44,7 +44,7 @@ import * as Schema from "effect/Schema"; import * as Semaphore from "effect/Semaphore"; import { HttpClient, HttpClientResponse } from "effect/unstable/http"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import { expandHomePath } from "../pathExpansion.ts"; import * as ServerSettings from "../serverSettings.ts"; import { resolveCodexHomeLayout } from "../provider/Drivers/CodexHomeLayout.ts"; @@ -153,7 +153,7 @@ export const make = Effect.gen(function* () { const crypto = yield* Crypto.Crypto; const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; - const config = yield* ServerConfig; + const config = yield* ServerConfig.ServerConfig; const settingsService = yield* ServerSettings.ServerSettingsService; const httpClient = yield* HttpClient.HttpClient; const hostEnvironment = yield* HostProcessEnvironment; diff --git a/apps/server/src/vcs/GitVcsDriverCore.test.ts b/apps/server/src/vcs/GitVcsDriverCore.test.ts index 7850c4a125dd..53a18551aaf0 100644 --- a/apps/server/src/vcs/GitVcsDriverCore.test.ts +++ b/apps/server/src/vcs/GitVcsDriverCore.test.ts @@ -28,7 +28,7 @@ import { type ReviewDiffFileContentsInput, type WorktreeSubmodules, } from "@t3tools/contracts"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import { gitCommandDuration } from "../observability/Metrics.ts"; import { makeGitVcsDriverCore, diff --git a/apps/server/src/vcs/GitVcsDriverCore.ts b/apps/server/src/vcs/GitVcsDriverCore.ts index 3068e3640872..61c84424219b 100644 --- a/apps/server/src/vcs/GitVcsDriverCore.ts +++ b/apps/server/src/vcs/GitVcsDriverCore.ts @@ -40,7 +40,7 @@ import { parseRemoteNamesInGitOrder, parseRemoteRefWithRemoteNames, } from "../git/remoteRefs.ts"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; const DEFAULT_TIMEOUT_MS = 30_000; const gitProcesses = Semaphore.makeUnsafe(8); @@ -828,7 +828,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const commandSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; - const { worktreesDir } = yield* ServerConfig; + const { worktreesDir } = yield* ServerConfig.ServerConfig; const crypto = yield* Crypto.Crypto; const executeRaw: GitVcsDriver.GitVcsDriver["Service"]["execute"] = Effect.fnUntraced( diff --git a/apps/server/src/ws.ts b/apps/server/src/ws.ts index ad8261e68710..8ba874310f15 100644 --- a/apps/server/src/ws.ts +++ b/apps/server/src/ws.ts @@ -1,6 +1,6 @@ import { OrchestrationDispatchCommandError } from "@t3tools/contracts"; import * as Crypto from "effect/Crypto"; -import { OrchestratorV2 } from "./orchestration-v2/Orchestrator.ts"; +import * as Orchestrator from "./orchestration-v2/Orchestrator.ts"; import * as NodeCrypto from "node:crypto"; import * as DateTime from "effect/DateTime"; @@ -112,7 +112,7 @@ import * as EnvironmentTheme from "./environmentTheme.ts"; import * as Keybindings from "./keybindings.ts"; import * as ExternalLauncher from "./process/externalLauncher.ts"; import * as ThreadManagementService from "./orchestration-v2/ThreadManagementService.ts"; -import { ProviderSessionManagerV2 } from "./orchestration-v2/ProviderSessionManager.ts"; +import * as ProviderSessionManager from "./orchestration-v2/ProviderSessionManager.ts"; import * as ThreadLaunchService from "./orchestration-v2/ThreadLaunchService.ts"; import * as ThreadMessageIntake from "./orchestration-v2/ThreadMessageIntake.ts"; import * as IdAllocator from "./orchestration-v2/IdAllocator.ts"; @@ -161,17 +161,12 @@ import { } from "./observability/RpcInstrumentation.ts"; import * as ProviderRegistry from "./provider/Services/ProviderRegistry.ts"; import * as ProviderInstanceRegistry from "./provider/Services/ProviderInstanceRegistry.ts"; -import { - AcpRegistryCatalog, - AcpRegistryError, - isAcpRegistryError, - toAcpRegistryOperationError, -} from "./provider/acp/AcpRegistrySupport.ts"; -import { AcpRegistryRuntimeCoordinator } from "./provider/acp/AcpRegistryRuntimeCoordinator.ts"; +import * as AcpRegistrySupport from "./provider/acp/AcpRegistrySupport.ts"; +import * as AcpRegistryRuntimeCoordinator from "./provider/acp/AcpRegistryRuntimeCoordinator.ts"; import * as ModelManifest from "./provider/ModelManifest.ts"; import * as ProviderMaintenance from "./provider/providerMaintenance.ts"; import * as ProviderMaintenanceRunner from "./provider/providerMaintenanceRunner.ts"; -import { ProviderAuthService } from "./provider/Services/ProviderAuthService.ts"; +import * as ProviderAuthService from "./provider/Services/ProviderAuthService.ts"; import { makeProviderInstallation } from "./provider/providerInstallation.ts"; import * as ServerSelfUpdate from "./cloud/selfUpdate.ts"; import * as ServerLifecycleEvents from "./serverLifecycleEvents.ts"; @@ -242,7 +237,7 @@ import { withUsageLimitsCommands, } from "@t3tools/shared/usageLimits"; import * as AgentSessionScanner from "./project/AgentSessionScanner.ts"; -import { AgentSessionImporter } from "./project/AgentSessionImporter.ts"; +import * as AgentSessionImporter from "./project/AgentSessionImporter.ts"; import * as UsageLimitSources from "./usage/UsageLimitSources.ts"; const CONFIG_DISCOVERY_TIMEOUT = Duration.seconds(5); @@ -1088,7 +1083,7 @@ const makeWsRpcLayer = ( const projectService = yield* ProjectService.ProjectService; const threadSearch = yield* ThreadSearch.ThreadSearch; - const providerSessionsV2 = yield* ProviderSessionManagerV2; + const providerSessionsV2 = yield* ProviderSessionManager.ProviderSessionManagerV2; const analytics = yield* AnalyticsService.AnalyticsService; // Client-origin attribution (#7774): every thread/turn the connecting // client starts is credited to its surface + app version. Best-effort: @@ -1103,14 +1098,14 @@ const makeWsRpcLayer = ( } }; const threadLaunch = yield* ThreadLaunchService.ThreadLaunchService; - const providerSessionManager = yield* ProviderSessionManagerV2; + const providerSessionManager = yield* ProviderSessionManager.ProviderSessionManagerV2; const scheduledTasks = yield* ScheduledTasks.ScheduledTaskService; const pullRequests = yield* PullRequestService.PullRequestService; const pullRequestSync = yield* PullRequestSyncReactor.PullRequestSyncReactor; const deviceService = yield* DeviceService.DeviceService; const deviceHostContext = yield* Effect.context>>(); - const orchestrationEngine = yield* OrchestratorV2; + const orchestrationEngine = yield* Orchestrator.OrchestratorV2; const crypto = yield* Crypto.Crypto; const serverCommandId = (tag: string) => crypto.randomUUIDv4.pipe( @@ -1134,7 +1129,7 @@ const makeWsRpcLayer = ( const repositoryIdentityResolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; const agentSessionScanner = yield* AgentSessionScanner.AgentSessionScanner; - const agentSessionImporter = yield* AgentSessionImporter; + const agentSessionImporter = yield* AgentSessionImporter.AgentSessionImporter; const checkpointDiffQuery = yield* CheckpointDiffQuery.CheckpointDiffQuery; const keybindings = yield* Keybindings.Keybindings; const environmentTheme = yield* EnvironmentTheme.EnvironmentThemeService; @@ -1151,10 +1146,11 @@ const makeWsRpcLayer = ( const modelManifest = yield* ModelManifest.ModelManifest; const providerVersionCache = yield* ProviderMaintenance.ProviderVersionCache; const providerInstances = yield* ProviderInstanceRegistry.ProviderInstanceRegistry; - const acpRegistryCatalog = yield* AcpRegistryCatalog; - const acpRegistryRuntimeCoordinator = yield* AcpRegistryRuntimeCoordinator; + const acpRegistryCatalog = yield* AcpRegistrySupport.AcpRegistryCatalog; + const acpRegistryRuntimeCoordinator = + yield* AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator; const providerMaintenanceRunner = yield* ProviderMaintenanceRunner.ProviderMaintenanceRunner; - const providerAuth = yield* ProviderAuthService; + const providerAuth = yield* ProviderAuthService.ProviderAuthService; const providerInstallation = yield* makeProviderInstallation(); const serverSelfUpdate = yield* ServerSelfUpdate.ServerSelfUpdate; const config = yield* ServerConfig.ServerConfig; @@ -2009,13 +2005,17 @@ const makeWsRpcLayer = ( [WS_METHODS.serverSearchAcpRegistry]: (input) => observeRpcEffect( WS_METHODS.serverSearchAcpRegistry, - acpRegistryCatalog.search(input).pipe(Effect.mapError(toAcpRegistryOperationError)), + acpRegistryCatalog + .search(input) + .pipe(Effect.mapError(AcpRegistrySupport.toAcpRegistryOperationError)), { "rpc.aggregate": "server" }, ), [WS_METHODS.serverPrepareAcpRegistryAgent]: (input) => observeRpcEffect( WS_METHODS.serverPrepareAcpRegistryAgent, - acpRegistryCatalog.prepare(input).pipe(Effect.mapError(toAcpRegistryOperationError)), + acpRegistryCatalog + .prepare(input) + .pipe(Effect.mapError(AcpRegistrySupport.toAcpRegistryOperationError)), { "rpc.aggregate": "server", "acp_registry.agent_id": input.agentId, @@ -2044,15 +2044,15 @@ const makeWsRpcLayer = ( ) .pipe( Effect.mapError((cause) => - isAcpRegistryError(cause) + AcpRegistrySupport.isAcpRegistryError(cause) ? cause - : new AcpRegistryError({ + : new AcpRegistrySupport.AcpRegistryError({ reason: "install_failed", detail: `Could not read provider settings while checking references for ACP Registry agent ${input.agentId}.`, cause, }), ), - Effect.mapError(toAcpRegistryOperationError), + Effect.mapError(AcpRegistrySupport.toAcpRegistryOperationError), ), { "rpc.aggregate": "server", @@ -3203,13 +3203,13 @@ const makeWsRpcLayer = ( result, commandId: serverCommandId("pr-created-link"), }).pipe( - Effect.provideService(OrchestratorV2, orchestrationEngine), + Effect.provideService(Orchestrator.OrchestratorV2, orchestrationEngine), Effect.provideService(ProjectService.ProjectService, projectService), ) ).pipe( Effect.andThen( refreshPushedPullRequests(input, result).pipe( - Effect.provideService(OrchestratorV2, orchestrationEngine), + Effect.provideService(Orchestrator.OrchestratorV2, orchestrationEngine), Effect.provideService(ProjectStore.ProjectStoreV2, projectStore), Effect.provideService( PullRequestService.PullRequestService,