From 55f9978fb95259b9437c375c057f69723a1d1bda Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 30 Sep 2026 01:35:54 -0700 Subject: [PATCH 1/4] refactor(server): import orchestration-v2 root services as namespaces Replace named tag and layer imports (and layer aliases) with module namespaces in the orchestration-v2 root. Imports and call sites only. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../AcpRegistryOrchestratorV2.live.test.ts | 50 +-- .../CheckpointCaptureService.test.ts | 18 +- .../CheckpointCaptureService.ts | 23 +- .../CheckpointRollbackService.test.ts | 91 +++-- .../CheckpointScopeOwnership.test.ts | 6 +- .../CheckpointService.test.ts | 16 +- .../src/orchestration-v2/CheckpointService.ts | 10 +- .../orchestration-v2/CommandPolicy.test.ts | 85 +++-- .../orchestration-v2/CommandReceiptStore.ts | 136 +++---- .../ContextHandoffService.test.ts | 18 +- .../orchestration-v2/ContextHandoffService.ts | 10 +- .../CursorOrchestratorV2.live.test.ts | 62 +-- .../DelegatedCompletionDelivery.test.ts | 67 ++-- .../src/orchestration-v2/EffectWorker.test.ts | 198 +++++----- .../src/orchestration-v2/EffectWorker.ts | 83 ++-- apps/server/src/orchestration-v2/EventSink.ts | 78 ++-- .../server/src/orchestration-v2/EventStore.ts | 132 ++++--- .../FoundationPersistence.test.ts | 277 +++++++------- .../GrokOrchestratorV2.live.test.ts | 50 +-- .../Orchestrator.control-reads.test.ts | 14 +- ...ProjectSettingsUpgrade.integration.test.ts | 36 +- .../ProjectionControlReads.test.ts | 40 +- .../orchestration-v2/ProjectionMaintenance.ts | 17 +- .../ProjectionRecovery.test.ts | 20 +- .../ProjectionSettlement.test.ts | 30 +- .../orchestration-v2/ProjectionStore.test.ts | 83 ++-- .../ProviderAdapterRegistry.test.ts | 38 +- .../ProviderAdapterRegistry.ts | 67 ++-- .../ProviderContinuationService.test.ts | 112 +++--- .../ProviderContinuationService.ts | 27 +- .../ProviderEventIngestor.test.ts | 131 ++++--- .../orchestration-v2/ProviderEventIngestor.ts | 18 +- .../orchestration-v2/ProviderFailure.test.ts | 6 +- .../ProviderRuntimeRecoveryService.ts | 15 +- .../ProviderSessionManager.test.ts | 293 +++++++------- .../ProviderSessionManager.ts | 30 +- .../ProviderTurnControlService.test.ts | 23 +- .../ProviderTurnControlService.ts | 10 +- .../ProviderTurnStartService.test.ts | 8 +- .../ProviderTurnStartService.ts | 72 ++-- .../PullRequestSyncReactor.test.ts | 27 +- .../PullRequestSyncReactor.ts | 12 +- .../RestartContinuation.test.ts | 22 +- .../orchestration-v2/RestartContinuation.ts | 8 +- .../RunCompletionReads.test.ts | 19 +- .../RunExecutionService.test.ts | 360 ++++++++++-------- .../orchestration-v2/RunExecutionService.ts | 47 ++- .../orchestration-v2/RuntimePolicy.test.ts | 14 +- .../src/orchestration-v2/RuntimePolicy.ts | 6 +- .../RuntimeRequestService.test.ts | 46 +-- .../orchestration-v2/RuntimeRequestService.ts | 10 +- .../SelectionRestart.integration.test.ts | 34 +- .../SteeringCompletion.integration.test.ts | 16 +- .../orchestration-v2/ThreadDeletion.test.ts | 10 +- .../ThreadFork.execution.test.ts | 8 +- .../ThreadForkService.test.ts | 33 +- .../ThreadManagementService.test.ts | 109 +++--- .../ThreadManagementService.ts | 56 ++- .../ThreadMessageIntake.test.ts | 16 +- .../ThreadPullRequestService.test.ts | 97 ++--- .../ThreadPullRequestService.ts | 4 +- .../ThreadSettlementService.test.ts | 181 +++++---- .../ThreadSettlementService.ts | 12 +- .../ThreadTitleRegenerationService.test.ts | 9 +- .../ThreadTitleRegenerationService.ts | 4 +- .../orchestration-v2/TurnStartReads.test.ts | 17 +- .../src/orchestration-v2/runtimeLayer.test.ts | 268 +++++++------ 67 files changed, 1991 insertions(+), 1954 deletions(-) diff --git a/apps/server/src/orchestration-v2/AcpRegistryOrchestratorV2.live.test.ts b/apps/server/src/orchestration-v2/AcpRegistryOrchestratorV2.live.test.ts index 22aa8a0dc042..b733aa19790b 100644 --- a/apps/server/src/orchestration-v2/AcpRegistryOrchestratorV2.live.test.ts +++ b/apps/server/src/orchestration-v2/AcpRegistryOrchestratorV2.live.test.ts @@ -1,5 +1,5 @@ import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; -import { SourceControlProviderRegistry } from "../sourceControl/SourceControlProviderRegistry.ts"; +import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it } from "@effect/vitest"; import { @@ -22,26 +22,23 @@ import { describe } from "vite-plus/test"; import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; import * as HostPowerMonitor from "../background/HostPowerMonitor.ts"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; -import { AntigravityInstallation } from "../provider/AntigravityInstallation.ts"; -import { CodexInstallation } from "../provider/CodexInstallation.ts"; -import { ServerEnvironmentIdentity } from "../environment/ServerEnvironment.ts"; +import * as AntigravityInstallation from "../provider/AntigravityInstallation.ts"; +import * as CodexInstallation from "../provider/CodexInstallation.ts"; +import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; import * as ModelManifest from "../provider/ModelManifest.ts"; import { ProviderInstanceRegistryHydrationLive } from "../provider/Layers/ProviderInstanceRegistryHydration.ts"; -import { - NoOpProviderEventLoggers, - ProviderEventLoggers, -} from "../provider/Layers/ProviderEventLoggers.ts"; -import { OpenCodeRuntimeLive } from "../provider/opencodeRuntime.ts"; +import * as ProviderEventLoggers from "../provider/Layers/ProviderEventLoggers.ts"; +import * as OpenCodeRuntime from "../provider/opencodeRuntime.ts"; import * as OpenCodeServerLedger from "../provider/OpenCodeServerLedger.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; +import * as ServerSettings from "../serverSettings.ts"; import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; import * as VcsProcess from "../vcs/VcsProcess.ts"; -import { OrchestratorV2 } from "./Orchestrator.ts"; +import * as Orchestrator from "./Orchestrator.ts"; import { worktreeRepairDependenciesTestLayer } from "./ProviderTurnStartService.testkit.ts"; import { OrchestrationV2LayerLive } from "./runtimeLayer.ts"; -import { layer as mcpSessionRegistryTestLayer } from "../mcp/McpSessionRegistry.testkit.ts"; +import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; // The Antigravity switch is a durable, named conformance fixture for Google's // official Registry distribution. It uses credentials already owned by the @@ -51,7 +48,9 @@ import { layer as mcpSessionRegistryTestLayer } from "../mcp/McpSessionRegistry. // src/orchestration-v2/AcpRegistryOrchestratorV2.live.test.ts const PlatformTestLayer = Layer.merge( NodeServices.layer, - Layer.mock(SourceControlProviderRegistry)({ resolveLink: () => Effect.die("unused title link") }), + Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ + resolveLink: () => Effect.die("unused title link"), + }), ); const runAntigravityFixture = process.env.T3_ACP_ANTIGRAVITY_LIVE === "1"; @@ -77,7 +76,7 @@ const vcsDriverRegistryLayer = VcsDriverRegistry.layer.pipe( const checkpointStoreLayer = CheckpointStore.layer.pipe(Layer.provide(vcsDriverRegistryLayer)); -const serverSettingsLayer = ServerSettingsService.layerTest({ +const serverSettingsLayer = ServerSettings.layerTest({ providerInstances: { [liveInstanceId]: { driver: ProviderDriverKind.make("acpRegistry"), @@ -105,20 +104,25 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe ), NodeServices.layer, FetchHttpClient.layer, - OpenCodeRuntimeLive.pipe( + OpenCodeRuntime.OpenCodeRuntimeLive.pipe( Layer.provide(OpenCodeServerLedger.layerTest), Layer.provide(PlatformTestLayer), ), - Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers), + Layer.succeed( + ProviderEventLoggers.ProviderEventLoggers, + ProviderEventLoggers.NoOpProviderEventLoggers, + ), ModelManifest.layerTest, - AntigravityInstallation.layer.pipe( + AntigravityInstallation.AntigravityInstallation.layer.pipe( Layer.provide(serverConfigLayer.pipe(Layer.provide(PlatformTestLayer))), Layer.provide(FetchHttpClient.layer), Layer.provide(PlatformTestLayer), ), // The Codex driver now resolves managed ChatGPT installs; these runs never launch Codex. - Layer.mock(CodexInstallation)({ managedDirectory: "unused-managed-installation" }), - Layer.succeed(ServerEnvironmentIdentity, { + Layer.mock(CodexInstallation.CodexInstallation)({ + managedDirectory: "unused-managed-installation", + }), + Layer.succeed(ServerEnvironment.ServerEnvironmentIdentity, { getEnvironmentId: Effect.succeed( EnvironmentId.make("00000000-0000-4000-8000-000000000001"), ), @@ -128,7 +132,7 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe ); const liveLayer = OrchestrationV2LayerLive.pipe( - Layer.provide(mcpSessionRegistryTestLayer), + Layer.provide(McpSessionRegistryTestkit.layer), Layer.provide(SqlitePersistenceMemory), Layer.provide(checkpointStoreLayer), Layer.provide(serverConfigLayer), @@ -144,7 +148,7 @@ const waitForIdle = Effect.fn("AcpRegistryOrchestratorV2Live.waitForIdle")(funct threadId: ThreadId, expectedRunCount: number, ) { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; for (let attempt = 0; attempt < 900; attempt += 1) { const projection = yield* orchestrator.getThreadProjection(threadId); if ( @@ -167,7 +171,7 @@ describe.runIf(runAntigravityFixture || process.env.T3_ACP_REGISTRY_LIVE_ORCHEST `runs and resumes ${runAntigravityFixture ? "Google Antigravity" : "a real registry agent"} through the production V2 harness`, () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const projectId = ProjectId.make("project:acp-registry-live"); const threadId = ThreadId.make("thread:acp-registry-live"); const marker = "ACP_REGISTRY_LIVE_7H3Q"; diff --git a/apps/server/src/orchestration-v2/CheckpointCaptureService.test.ts b/apps/server/src/orchestration-v2/CheckpointCaptureService.test.ts index 24b0711e3e11..d9efb6232af5 100644 --- a/apps/server/src/orchestration-v2/CheckpointCaptureService.test.ts +++ b/apps/server/src/orchestration-v2/CheckpointCaptureService.test.ts @@ -24,9 +24,9 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; import { VcsProcessTimeoutError } from "@t3tools/contracts"; -import { CheckpointServiceV2, layer as checkpointServiceLayer } from "./CheckpointService.ts"; +import * as CheckpointService from "./CheckpointService.ts"; import * as CheckpointCaptureService from "./CheckpointCaptureService.ts"; -import { EventSinkV2 } from "./EventSink.ts"; +import * as EventSink from "./EventSink.ts"; import * as IdAllocator from "./IdAllocator.ts"; import * as ProjectionStore from "./ProjectionStore.ts"; @@ -270,7 +270,7 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { Layer.mergeAll( IdAllocator.layer, refLookupFails - ? checkpointServiceLayer.pipe( + ? CheckpointService.layer.pipe( Layer.provide( Layer.mergeAll( IdAllocator.layer, @@ -290,14 +290,14 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { ), ), ) - : Layer.mock(CheckpointServiceV2)({ + : Layer.mock(CheckpointService.CheckpointServiceV2)({ materializeBaselineCheckpoint: () => Effect.die( "baseline materialization must be skipped when ordinal 0 is ready", ), capture: () => Effect.succeed(captured), }), - Layer.mock(EventSinkV2)({ + Layer.mock(EventSink.EventSinkV2)({ commitCommand: (input) => Ref.set(committed, input.events).pipe( Effect.as({ @@ -455,11 +455,11 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { Layer.provide( Layer.mergeAll( IdAllocator.layer, - Layer.mock(CheckpointServiceV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ materializeBaselineCheckpoint: () => Effect.die("a discarded run has no baseline"), capture: () => Effect.die("a discarded run must not be captured"), }), - Layer.mock(EventSinkV2)({ + Layer.mock(EventSink.EventSinkV2)({ commitCommand: (input) => Ref.set(committed, input.events).pipe(Effect.as({ committed: true } as never)), }), @@ -684,14 +684,14 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { Layer.provide( Layer.mergeAll( IdAllocator.layer, - Layer.mock(CheckpointServiceV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ materializeBaselineCheckpoint: () => Effect.die("baseline materialization must be skipped when ordinal 0 is ready"), capture: () => Effect.succeed(captured), }), // Commit straight into the projection so the test reads what a // client would see after the capture lands. - Layer.mock(EventSinkV2)({ + Layer.mock(EventSink.EventSinkV2)({ commitCommand: (input) => Effect.forEach(input.events, (event) => projectionStore.apply(event)).pipe( Effect.andThen(Ref.update(commits, (count) => count + 1)), diff --git a/apps/server/src/orchestration-v2/CheckpointCaptureService.ts b/apps/server/src/orchestration-v2/CheckpointCaptureService.ts index 14f284615c0c..ab34b62367b5 100644 --- a/apps/server/src/orchestration-v2/CheckpointCaptureService.ts +++ b/apps/server/src/orchestration-v2/CheckpointCaptureService.ts @@ -15,10 +15,10 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; -import { CheckpointServiceV2 } from "./CheckpointService.ts"; -import { EventSinkV2 } from "./EventSink.ts"; -import { IdAllocatorV2, type IdAllocatorV2Shape } from "./IdAllocator.ts"; -import { ProjectionStoreV2 } from "./ProjectionStore.ts"; +import * as CheckpointService from "./CheckpointService.ts"; +import * as EventSink from "./EventSink.ts"; +import * as IdAllocator from "./IdAllocator.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; export class CheckpointCaptureExecutionError extends Schema.TaggedError()( "CheckpointCaptureExecutionError", @@ -48,14 +48,17 @@ export class CheckpointCaptureServiceV2 extends Context.Service< export const layer: Layer.Layer< CheckpointCaptureServiceV2, never, - CheckpointServiceV2 | EventSinkV2 | IdAllocatorV2 | ProjectionStoreV2 + | CheckpointService.CheckpointServiceV2 + | EventSink.EventSinkV2 + | IdAllocator.IdAllocatorV2 + | ProjectionStore.ProjectionStoreV2 > = Layer.effect( CheckpointCaptureServiceV2, Effect.gen(function* () { - const checkpoints = yield* CheckpointServiceV2; - const eventSink = yield* EventSinkV2; - const ids = yield* IdAllocatorV2; - const projections = yield* ProjectionStoreV2; + const checkpoints = yield* CheckpointService.CheckpointServiceV2; + const eventSink = yield* EventSink.EventSinkV2; + const ids = yield* IdAllocator.IdAllocatorV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const execute = Effect.fn("orchestrationV2.checkpointCapture.execute")(function* (input: { readonly threadId: ThreadId; @@ -249,7 +252,7 @@ export const layer: Layer.Layer< ); function makeCheckpointTurnItem(input: { - readonly idAllocator: IdAllocatorV2Shape; + readonly idAllocator: IdAllocator.IdAllocatorV2Shape; readonly run: OrchestrationV2Run; readonly rootNode: OrchestrationV2ExecutionNode; readonly providerThread: OrchestrationV2ProviderThread; diff --git a/apps/server/src/orchestration-v2/CheckpointRollbackService.test.ts b/apps/server/src/orchestration-v2/CheckpointRollbackService.test.ts index 3330c5f135b9..62246f3e3df2 100644 --- a/apps/server/src/orchestration-v2/CheckpointRollbackService.test.ts +++ b/apps/server/src/orchestration-v2/CheckpointRollbackService.test.ts @@ -18,24 +18,21 @@ import * as Layer from "effect/Layer"; import { resolveCodexRollbackTurnCount } from "./Adapters/CodexAdapterV2.ts"; import { isCheckpointRestoreIsolated } from "./CheckpointRestoreSafety.ts"; -import { CheckpointServiceV2 } from "./CheckpointService.ts"; -import { - CheckpointRollbackServiceV2, - layer as checkpointRollbackLayer, -} from "./CheckpointRollbackService.ts"; -import { EventSinkV2 } from "./EventSink.ts"; -import { layer as idAllocatorLayer } from "./IdAllocator.ts"; -import { ProjectionStoreV2 } from "./ProjectionStore.ts"; +import * as CheckpointService from "./CheckpointService.ts"; +import * as CheckpointRollbackService from "./CheckpointRollbackService.ts"; +import * as EventSink from "./EventSink.ts"; +import * as IdAllocator from "./IdAllocator.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; import * as ProjectStore from "./ProjectStore.ts"; import type { ProviderAdapterV2RollbackThreadInput } from "./ProviderAdapter.ts"; -import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts"; -import { RuntimePolicyV2 } from "./RuntimePolicy.ts"; +import * as ProviderSessionManager from "./ProviderSessionManager.ts"; +import * as RuntimePolicy from "./RuntimePolicy.ts"; // A root that does not exist never overlaps, so other owners decide isolation. const unrelatedProject = Option.some({ workspaceRoot: "/nonexistent/t3-rollback-project", } as never); -const checkpointRollbackServiceLayer = checkpointRollbackLayer.pipe( +const checkpointRollbackServiceLayer = CheckpointRollbackService.layer.pipe( Layer.provide( Layer.mergeAll( NodeServices.layer, @@ -67,10 +64,10 @@ it.effect("rejects a non-ready checkpoint before opening a session or restoring const testLayer = checkpointRollbackServiceLayer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ restore }), - Layer.mock(EventSinkV2)({}), - idAllocatorLayer, - Layer.mock(ProjectionStoreV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ restore }), + Layer.mock(EventSink.EventSinkV2)({}), + IdAllocator.layer, + Layer.mock(ProjectionStore.ProjectionStoreV2)({ getThreadRecords: () => Effect.succeed(projection), getShellSnapshot: () => Effect.succeed({ @@ -80,14 +77,14 @@ it.effect("rejects a non-ready checkpoint before opening a session or restoring archivedThreads: [], }), }), - Layer.mock(ProviderSessionManagerV2)({ open }), - Layer.mock(RuntimePolicyV2)({ resolve: resolveRuntimePolicy }), + Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ open }), + Layer.mock(RuntimePolicy.RuntimePolicyV2)({ resolve: resolveRuntimePolicy }), ), ), ); return Effect.gen(function* () { - const service = yield* CheckpointRollbackServiceV2; + const service = yield* CheckpointRollbackService.CheckpointRollbackServiceV2; const error = yield* service .execute({ threadId, @@ -144,10 +141,10 @@ it.effect("rejects a rollback when another provider thread became active", () => const testLayer = checkpointRollbackServiceLayer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ restore }), - Layer.mock(EventSinkV2)({}), - idAllocatorLayer, - Layer.mock(ProjectionStoreV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ restore }), + Layer.mock(EventSink.EventSinkV2)({}), + IdAllocator.layer, + Layer.mock(ProjectionStore.ProjectionStoreV2)({ getThreadRecords: () => Effect.succeed(projection), getShellSnapshot: () => Effect.succeed({ @@ -157,14 +154,14 @@ it.effect("rejects a rollback when another provider thread became active", () => archivedThreads: [], }), }), - Layer.mock(ProviderSessionManagerV2)({ open }), - Layer.mock(RuntimePolicyV2)({ resolve: resolveRuntimePolicy }), + Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ open }), + Layer.mock(RuntimePolicy.RuntimePolicyV2)({ resolve: resolveRuntimePolicy }), ), ), ); return Effect.gen(function* () { - const service = yield* CheckpointRollbackServiceV2; + const service = yield* CheckpointRollbackService.CheckpointRollbackServiceV2; const error = yield* service .execute({ threadId, @@ -224,10 +221,10 @@ it.effect("rejects a rollback when provider selection changed before execution", const testLayer = checkpointRollbackServiceLayer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ restore }), - Layer.mock(EventSinkV2)({}), - idAllocatorLayer, - Layer.mock(ProjectionStoreV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ restore }), + Layer.mock(EventSink.EventSinkV2)({}), + IdAllocator.layer, + Layer.mock(ProjectionStore.ProjectionStoreV2)({ getThreadRecords: () => Effect.succeed(projection), getShellSnapshot: () => Effect.succeed({ @@ -237,14 +234,14 @@ it.effect("rejects a rollback when provider selection changed before execution", archivedThreads: [], }), }), - Layer.mock(ProviderSessionManagerV2)({ open }), - Layer.mock(RuntimePolicyV2)({ resolve: resolveRuntimePolicy }), + Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ open }), + Layer.mock(RuntimePolicy.RuntimePolicyV2)({ resolve: resolveRuntimePolicy }), ), ), ); return Effect.gen(function* () { - const service = yield* CheckpointRollbackServiceV2; + const service = yield* CheckpointRollbackService.CheckpointRollbackServiceV2; const error = yield* service .execute({ threadId, @@ -295,10 +292,10 @@ it.effect("reports a missing provider turn as a structured rollback failure", () const testLayer = checkpointRollbackServiceLayer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ restore }), - Layer.mock(EventSinkV2)({}), - idAllocatorLayer, - Layer.mock(ProjectionStoreV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ restore }), + Layer.mock(EventSink.EventSinkV2)({}), + IdAllocator.layer, + Layer.mock(ProjectionStore.ProjectionStoreV2)({ getThreadRecords: () => Effect.succeed(projection), getShellSnapshot: () => Effect.succeed({ @@ -308,10 +305,10 @@ it.effect("reports a missing provider turn as a structured rollback failure", () archivedThreads: [], }), }), - Layer.mock(ProviderSessionManagerV2)({ + Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ open: () => Effect.succeed({} as never), }), - Layer.mock(RuntimePolicyV2)({ + Layer.mock(RuntimePolicy.RuntimePolicyV2)({ resolve: () => Effect.succeed({} as never), }), ), @@ -319,7 +316,7 @@ it.effect("reports a missing provider turn as a structured rollback failure", () ); return Effect.gen(function* () { - const service = yield* CheckpointRollbackServiceV2; + const service = yield* CheckpointRollbackService.CheckpointRollbackServiceV2; const error = yield* service .execute({ threadId, @@ -393,13 +390,13 @@ it.effect.each([ const testLayer = checkpointRollbackServiceLayer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ restore: () => Effect.sync(() => { calls.push("files"); }), }), - Layer.mock(EventSinkV2)({ + Layer.mock(EventSink.EventSinkV2)({ write: ({ events }) => Effect.sync(() => { assert.ok( @@ -411,8 +408,8 @@ it.effect.each([ return []; }), }), - idAllocatorLayer, - Layer.mock(ProjectionStoreV2)({ + IdAllocator.layer, + Layer.mock(ProjectionStore.ProjectionStoreV2)({ getThreadRecords: () => Effect.succeed(projection), getThreadProviderContext: () => Effect.succeed({ providerSessions: [] } as never), getCheckpointContext: () => @@ -438,7 +435,7 @@ it.effect.each([ : [], }), }), - Layer.mock(ProviderSessionManagerV2)({ + Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ open: () => Effect.succeed({ rollbackThread: (input: ProviderAdapterV2RollbackThreadInput) => @@ -450,12 +447,12 @@ it.effect.each([ }), } as never), }), - Layer.mock(RuntimePolicyV2)({ resolve: () => Effect.succeed({} as never) }), + Layer.mock(RuntimePolicy.RuntimePolicyV2)({ resolve: () => Effect.succeed({} as never) }), ), ), ); return Effect.gen(function* () { - const service = yield* CheckpointRollbackServiceV2; + const service = yield* CheckpointRollbackService.CheckpointRollbackServiceV2; if (restoreFiles && shared !== "none") { const error = yield* Effect.flip( service.execute({ threadId, providerThreadId, checkpointId, scopeId, restoreFiles }), @@ -483,7 +480,7 @@ it.effect.skipIf(!symlinksSupported)( yield* fileSystem.symlink(cwd, alias); const threadId = ThreadId.make("restore-alias-current"); const otherId = ThreadId.make("restore-alias-archived"); - const projections = ProjectionStoreV2.of({ + const projections = ProjectionStore.ProjectionStoreV2.of({ getShellSnapshot: () => Effect.succeed({ schemaVersion: 1, diff --git a/apps/server/src/orchestration-v2/CheckpointScopeOwnership.test.ts b/apps/server/src/orchestration-v2/CheckpointScopeOwnership.test.ts index 7968001558a4..31a6358ca49c 100644 --- a/apps/server/src/orchestration-v2/CheckpointScopeOwnership.test.ts +++ b/apps/server/src/orchestration-v2/CheckpointScopeOwnership.test.ts @@ -15,7 +15,7 @@ import { } from "@t3tools/contracts"; import * as CheckpointDiffQuery from "../checkpointing/CheckpointDiffQuery.ts"; import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; -import { IdAllocatorV2, layer as idAllocatorLayer } from "./IdAllocator.ts"; +import * as IdAllocator from "./IdAllocator.ts"; import { checkpointRefForScopeOrdinal } from "./CheckpointService.ts"; import * as ProjectionStore from "./ProjectionStore.ts"; import * as ThreadManagement from "./ThreadManagementService.ts"; @@ -23,11 +23,11 @@ import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; const projectionLayer = Layer.mergeAll( ProjectionStore.layer.pipe(Layer.provideMerge(SqlitePersistenceMemory)), SqlitePersistenceMemory, - idAllocatorLayer, + IdAllocator.layer, ); it.effect("resolves the thread baseline after a second root run replaces scope ownership", () => Effect.gen(function* () { - const ids = yield* IdAllocatorV2; + const ids = yield* IdAllocator.IdAllocatorV2; const projections = yield* ProjectionStore.ProjectionStoreV2; const now = DateTime.makeUnsafe("2026-09-08T12:00:00.000Z"); const threadId = ThreadId.make("thread:audit-root-scope"); diff --git a/apps/server/src/orchestration-v2/CheckpointService.test.ts b/apps/server/src/orchestration-v2/CheckpointService.test.ts index 1595096a778c..b03437700ac9 100644 --- a/apps/server/src/orchestration-v2/CheckpointService.test.ts +++ b/apps/server/src/orchestration-v2/CheckpointService.test.ts @@ -14,12 +14,8 @@ import * as Exit from "effect/Exit"; import * as Layer from "effect/Layer"; import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; -import { - CheckpointServiceV2, - checkpointRefForScopeOrdinal, - layer as checkpointServiceLayer, -} from "./CheckpointService.ts"; -import { layer as idAllocatorLayer } from "./IdAllocator.ts"; +import * as CheckpointService from "./CheckpointService.ts"; +import * as IdAllocator from "./IdAllocator.ts"; it.effect.each([false, true, "interrupt"] as const)( "materializes baseline, lookup fails=%s", @@ -51,10 +47,10 @@ it.effect.each([false, true, "interrupt"] as const)( ) : Effect.succeed(true), ); - const testLayer = checkpointServiceLayer.pipe( + const testLayer = CheckpointService.layer.pipe( Layer.provide( Layer.mergeAll( - idAllocatorLayer, + IdAllocator.layer, Layer.mock(CheckpointStore.CheckpointStore)({ isGitRepository: () => Effect.succeed(true), hasCheckpointRef, @@ -65,7 +61,7 @@ it.effect.each([false, true, "interrupt"] as const)( ); return Effect.gen(function* () { - const checkpoints = yield* CheckpointServiceV2; + const checkpoints = yield* CheckpointService.CheckpointServiceV2; if (lookupFails === "interrupt") { const exit = yield* Effect.exit( checkpoints.materializeBaselineCheckpoint({ scope, ordinalWithinScope: 2 }), @@ -92,7 +88,7 @@ it.effect.each([false, true, "interrupt"] as const)( assert.equal(baseline.ordinalWithinScope, 2); assert.equal( baseline.ref, - checkpointRefForScopeOrdinal({ + CheckpointService.checkpointRefForScopeOrdinal({ scopeId: scope.id, ordinalWithinScope: 2, }), diff --git a/apps/server/src/orchestration-v2/CheckpointService.ts b/apps/server/src/orchestration-v2/CheckpointService.ts index 842579c5127c..1471de750357 100644 --- a/apps/server/src/orchestration-v2/CheckpointService.ts +++ b/apps/server/src/orchestration-v2/CheckpointService.ts @@ -21,7 +21,7 @@ import * as Semaphore from "effect/Semaphore"; import { parseTurnDiffFilesFromNumstat } from "../checkpointing/Diffs.ts"; import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; -import { IdAllocatorV2, type IdAllocatorV2Shape } from "./IdAllocator.ts"; +import * as IdAllocator from "./IdAllocator.ts"; const CHECKPOINT_REFS_PREFIX = "refs/t3/orchestration-v2/checkpoints"; const ROOT_CHECKPOINT_SCOPE_NAME = "root"; @@ -169,7 +169,7 @@ export function checkpointRefForScopeOrdinal(input: { } function checkpointIdForScopeOrdinal( - idAllocator: IdAllocatorV2Shape, + idAllocator: IdAllocator.IdAllocatorV2Shape, input: { readonly scopeId: CheckpointScopeId; readonly ordinalWithinScope: number; @@ -182,7 +182,7 @@ function checkpointIdForScopeOrdinal( } function makeRootRunScope(input: { - readonly idAllocator: IdAllocatorV2Shape; + readonly idAllocator: IdAllocator.IdAllocatorV2Shape; readonly threadId: ThreadId; readonly runId: RunId; readonly rootNodeId: NodeId; @@ -243,12 +243,12 @@ function makeCheckpoint(input: { export const layer: Layer.Layer< CheckpointServiceV2, never, - CheckpointStore.CheckpointStore | IdAllocatorV2 + CheckpointStore.CheckpointStore | IdAllocator.IdAllocatorV2 > = Layer.effect( CheckpointServiceV2, Effect.gen(function* () { const checkpointStore = yield* CheckpointStore.CheckpointStore; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const workspaceSemaphores = yield* Ref.make(new Map()); const getWorkspaceSemaphore = (cwd: string) => diff --git a/apps/server/src/orchestration-v2/CommandPolicy.test.ts b/apps/server/src/orchestration-v2/CommandPolicy.test.ts index d663184dfd46..256ecd45e75a 100644 --- a/apps/server/src/orchestration-v2/CommandPolicy.test.ts +++ b/apps/server/src/orchestration-v2/CommandPolicy.test.ts @@ -14,14 +14,7 @@ import * as Effect from "effect/Effect"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; import { CursorProviderCapabilitiesV2 } from "./Adapters/CursorAdapterV2.ts"; import { GrokProviderCapabilitiesV2 } from "./Adapters/GrokAdapterV2.ts"; -import { - type CommandPolicyCapability, - CommandPolicyCapabilityUnsupportedError, - CommandPolicyV2, - layer as commandPolicyLayer, - resolveMessageDispatchIntent, - type SteeringExecutionPolicyV2, -} from "./CommandPolicy.ts"; +import * as CommandPolicy from "./CommandPolicy.ts"; const commandId = CommandId.make("command-policy-test"); const threadId = ThreadId.make("command-policy-thread"); @@ -56,11 +49,15 @@ function dispatchProjection( it("resolves automatic message delivery from authoritative provider capabilities", () => { assert.deepEqual( - resolveMessageDispatchIntent(dispatchProjection(), { type: "start_immediately" }, "auto"), + CommandPolicy.resolveMessageDispatchIntent( + dispatchProjection(), + { type: "start_immediately" }, + "auto", + ), { type: "start_immediately" }, ); assert.deepEqual( - resolveMessageDispatchIntent( + CommandPolicy.resolveMessageDispatchIntent( dispatchProjection(baseCapabilities), { type: "start_immediately" }, "auto", @@ -68,7 +65,7 @@ it("resolves automatic message delivery from authoritative provider capabilities { type: "steer_active", targetRunId: activeRunId }, ); assert.deepEqual( - resolveMessageDispatchIntent( + CommandPolicy.resolveMessageDispatchIntent( dispatchProjection( capabilities((current) => ({ ...current, @@ -86,7 +83,7 @@ it("resolves automatic message delivery from authoritative provider capabilities { type: "queue_after_active" }, ); assert.deepEqual( - resolveMessageDispatchIntent( + CommandPolicy.resolveMessageDispatchIntent( dispatchProjection( capabilities((current) => ({ ...current, @@ -110,7 +107,7 @@ it.each(["preparing", "starting"] as const)( (status) => { const projection = dispatchProjection(baseCapabilities); assert.deepEqual( - resolveMessageDispatchIntent( + CommandPolicy.resolveMessageDispatchIntent( { ...projection, runs: projection.runs.map((run) => ({ ...run, status })) }, { type: "start_immediately" }, "auto", @@ -123,25 +120,33 @@ it.each(["preparing", "starting"] as const)( it("targets the latest active run for explicit steer and restart intent", () => { const projection = dispatchProjection(baseCapabilities); assert.deepEqual( - resolveMessageDispatchIntent(projection, { type: "start_immediately" }, "steer"), + CommandPolicy.resolveMessageDispatchIntent(projection, { type: "start_immediately" }, "steer"), { type: "steer_active", targetRunId: activeRunId }, ); assert.deepEqual( - resolveMessageDispatchIntent(projection, { type: "start_immediately" }, "restart"), + CommandPolicy.resolveMessageDispatchIntent( + projection, + { type: "start_immediately" }, + "restart", + ), { type: "restart_active", targetRunId: activeRunId }, ); assert.deepEqual( - resolveMessageDispatchIntent(dispatchProjection(), { type: "start_immediately" }, "steer"), + CommandPolicy.resolveMessageDispatchIntent( + dispatchProjection(), + { type: "start_immediately" }, + "steer", + ), { type: "start_immediately" }, ); }); -const layer = it.layer(commandPolicyLayer); +const layer = it.layer(CommandPolicy.layer); layer("CommandPolicyV2", (it) => { it.effect("prefers direct active steering when the provider supports it", () => Effect.gen(function* () { - const policy = yield* CommandPolicyV2; + const policy = yield* CommandPolicy.CommandPolicyV2; const result = yield* policy.decideSteeringExecution({ commandId, @@ -156,7 +161,7 @@ layer("CommandPolicyV2", (it) => { it.effect("uses interrupt-and-restart steering when direct steering is unavailable", () => Effect.gen(function* () { - const policy = yield* CommandPolicyV2; + const policy = yield* CommandPolicy.CommandPolicyV2; const result = yield* policy.decideSteeringExecution({ commandId, @@ -179,7 +184,7 @@ layer("CommandPolicyV2", (it) => { it.effect("uses interrupt-and-restart steering for Grok ACP", () => Effect.gen(function* () { - const policy = yield* CommandPolicyV2; + const policy = yield* CommandPolicy.CommandPolicyV2; const result = yield* policy.decideSteeringExecution({ commandId, @@ -194,7 +199,7 @@ layer("CommandPolicyV2", (it) => { it.effect("honors an explicit interrupt-and-restart request", () => Effect.gen(function* () { - const policy = yield* CommandPolicyV2; + const policy = yield* CommandPolicy.CommandPolicyV2; const result = yield* policy.decideSteeringExecution({ commandId, @@ -210,7 +215,7 @@ layer("CommandPolicyV2", (it) => { it.effect("reports the actually missing capability across the steering matrix", () => Effect.gen(function* () { - const policy = yield* CommandPolicyV2; + const policy = yield* CommandPolicy.CommandPolicyV2; const providerInstanceId = ProviderInstanceId.make("codex"); const fallbackDetail = "providerInstanceId cannot steer active turns directly or by interrupt-and-restart"; @@ -223,10 +228,10 @@ layer("CommandPolicyV2", (it) => { readonly supportsInterrupt: boolean; readonly supportsSteeringByInterruptRestart: boolean; readonly expected: - | { readonly type: "policy"; readonly value: SteeringExecutionPolicyV2 } + | { readonly type: "policy"; readonly value: CommandPolicy.SteeringExecutionPolicyV2 } | { readonly type: "error"; - readonly capability: CommandPolicyCapability; + readonly capability: CommandPolicy.CommandPolicyCapability; readonly detail: string; }; }> = [ @@ -401,7 +406,7 @@ layer("CommandPolicyV2", (it) => { } const error = yield* decision.pipe(Effect.flip); - assert.instanceOf(error, CommandPolicyCapabilityUnsupportedError); + assert.instanceOf(error, CommandPolicy.CommandPolicyCapabilityUnsupportedError); assert.equal(error.commandId, commandId); assert.equal(error.threadId, threadId); assert.equal(error.providerInstanceId, providerInstanceId); @@ -413,7 +418,7 @@ layer("CommandPolicyV2", (it) => { it.effect("returns typed capability errors for unsupported active steering", () => Effect.gen(function* () { - const policy = yield* CommandPolicyV2; + const policy = yield* CommandPolicy.CommandPolicyV2; const error = yield* policy .decideSteeringExecution({ @@ -432,14 +437,14 @@ layer("CommandPolicyV2", (it) => { }) .pipe(Effect.flip); - assert.instanceOf(error, CommandPolicyCapabilityUnsupportedError); + assert.instanceOf(error, CommandPolicy.CommandPolicyCapabilityUnsupportedError); assert.equal(error.capability, "active_steering"); }), ); it.effect("guards native fork behind fork and identity capabilities", () => Effect.gen(function* () { - const policy = yield* CommandPolicyV2; + const policy = yield* CommandPolicy.CommandPolicyV2; const error = yield* policy .ensureNativeFork({ @@ -457,14 +462,14 @@ layer("CommandPolicyV2", (it) => { }) .pipe(Effect.flip); - assert.instanceOf(error, CommandPolicyCapabilityUnsupportedError); + assert.instanceOf(error, CommandPolicy.CommandPolicyCapabilityUnsupportedError); assert.equal(error.capability, "native_fork"); }), ); it.effect("uses a native fork when the provider supports the requested source point", () => Effect.gen(function* () { - const policy = yield* CommandPolicyV2; + const policy = yield* CommandPolicy.CommandPolicyV2; const result = yield* policy.decideForkExecution({ commandId, @@ -483,7 +488,7 @@ layer("CommandPolicyV2", (it) => { it.effect("falls back to portable context when Cursor cannot fork natively", () => Effect.gen(function* () { - const policy = yield* CommandPolicyV2; + const policy = yield* CommandPolicy.CommandPolicyV2; const result = yield* policy.decideForkExecution({ commandId, @@ -502,7 +507,7 @@ layer("CommandPolicyV2", (it) => { it.effect("falls back to portable context when Grok ACP cannot fork natively", () => Effect.gen(function* () { - const policy = yield* CommandPolicyV2; + const policy = yield* CommandPolicy.CommandPolicyV2; const result = yield* policy.decideForkExecution({ commandId, @@ -521,7 +526,7 @@ layer("CommandPolicyV2", (it) => { it.effect("returns a typed error when neither native nor portable fork is available", () => Effect.gen(function* () { - const policy = yield* CommandPolicyV2; + const policy = yield* CommandPolicy.CommandPolicyV2; const error = yield* policy .decideForkExecution({ @@ -546,14 +551,14 @@ layer("CommandPolicyV2", (it) => { }) .pipe(Effect.flip); - assert.instanceOf(error, CommandPolicyCapabilityUnsupportedError); + assert.instanceOf(error, CommandPolicy.CommandPolicyCapabilityUnsupportedError); assert.equal(error.capability, "context_handoff"); }), ); it.effect("guards rollback behind provider rollback snapshot support", () => Effect.gen(function* () { - const policy = yield* CommandPolicyV2; + const policy = yield* CommandPolicy.CommandPolicyV2; const error = yield* policy .ensureRollback({ @@ -570,14 +575,14 @@ layer("CommandPolicyV2", (it) => { }) .pipe(Effect.flip); - assert.instanceOf(error, CommandPolicyCapabilityUnsupportedError); + assert.instanceOf(error, CommandPolicy.CommandPolicyCapabilityUnsupportedError); assert.equal(error.capability, "rollback_snapshot"); }), ); it.effect("guards fork-delta handoff behind context handoff capabilities", () => Effect.gen(function* () { - const policy = yield* CommandPolicyV2; + const policy = yield* CommandPolicy.CommandPolicyV2; const error = yield* policy .ensureContextHandoff({ @@ -595,14 +600,14 @@ layer("CommandPolicyV2", (it) => { }) .pipe(Effect.flip); - assert.instanceOf(error, CommandPolicyCapabilityUnsupportedError); + assert.instanceOf(error, CommandPolicy.CommandPolicyCapabilityUnsupportedError); assert.equal(error.capability, "context_handoff"); }), ); it.effect("guards queued turns behind queued-message support", () => Effect.gen(function* () { - const policy = yield* CommandPolicyV2; + const policy = yield* CommandPolicy.CommandPolicyV2; const error = yield* policy .ensureQueuedMessages({ @@ -619,7 +624,7 @@ layer("CommandPolicyV2", (it) => { }) .pipe(Effect.flip); - assert.instanceOf(error, CommandPolicyCapabilityUnsupportedError); + assert.instanceOf(error, CommandPolicy.CommandPolicyCapabilityUnsupportedError); assert.equal(error.capability, "queued_messages"); }), ); diff --git a/apps/server/src/orchestration-v2/CommandReceiptStore.ts b/apps/server/src/orchestration-v2/CommandReceiptStore.ts index 19ebfef0b593..338bcf72b022 100644 --- a/apps/server/src/orchestration-v2/CommandReceiptStore.ts +++ b/apps/server/src/orchestration-v2/CommandReceiptStore.ts @@ -7,10 +7,7 @@ import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; import { OrchestrationCommandReceiptRepositoryLive } from "../persistence/Layers/OrchestrationCommandReceipts.ts"; -import { - OrchestrationCommandReceiptRepository, - type OrchestrationCommandReceipt, -} from "../persistence/Services/OrchestrationCommandReceipts.ts"; +import * as OrchestrationCommandReceipts from "../persistence/Services/OrchestrationCommandReceipts.ts"; /** * ERRORS @@ -114,7 +111,7 @@ const decodeProjectReceipt = Schema.decodeUnknownEffect( })), ); -function fromApplicationReceipt(receipt: OrchestrationCommandReceipt) { +function fromApplicationReceipt(receipt: OrchestrationCommandReceipts.OrchestrationCommandReceipt) { return decodeReceipt({ commandId: receipt.commandId, threadId: receipt.aggregateId, @@ -126,7 +123,9 @@ function fromApplicationReceipt(receipt: OrchestrationCommandReceipt) { }); } -function toApplicationReceipt(receipt: AnyCommandReceiptV2): OrchestrationCommandReceipt { +function toApplicationReceipt( + receipt: AnyCommandReceiptV2, +): OrchestrationCommandReceipts.OrchestrationCommandReceipt { return { commandId: receipt.commandId, ...("projectId" in receipt @@ -140,72 +139,75 @@ function toApplicationReceipt(receipt: AnyCommandReceiptV2): OrchestrationComman }; } -const baseLayer: Layer.Layer = - Layer.effect( - CommandReceiptStoreV2, - Effect.gen(function* () { - const receipts = yield* OrchestrationCommandReceiptRepository; - - return CommandReceiptStoreV2.of({ - insertIfAbsent: (receipt) => - receipts.insertIfAbsent(toApplicationReceipt(receipt)).pipe( - Effect.mapError( - (cause) => - new CommandReceiptStoreWriteError({ - commandId: receipt.commandId, - cause, - }), - ), +const baseLayer: Layer.Layer< + CommandReceiptStoreV2, + never, + OrchestrationCommandReceipts.OrchestrationCommandReceiptRepository +> = Layer.effect( + CommandReceiptStoreV2, + Effect.gen(function* () { + const receipts = yield* OrchestrationCommandReceipts.OrchestrationCommandReceiptRepository; + + return CommandReceiptStoreV2.of({ + insertIfAbsent: (receipt) => + receipts.insertIfAbsent(toApplicationReceipt(receipt)).pipe( + Effect.mapError( + (cause) => + new CommandReceiptStoreWriteError({ + commandId: receipt.commandId, + cause, + }), ), - upsert: (receipt) => - receipts.upsert(toApplicationReceipt(receipt)).pipe( - Effect.mapError( - (cause) => - new CommandReceiptStoreWriteError({ - commandId: receipt.commandId, - cause, - }), - ), + ), + upsert: (receipt) => + receipts.upsert(toApplicationReceipt(receipt)).pipe( + Effect.mapError( + (cause) => + new CommandReceiptStoreWriteError({ + commandId: receipt.commandId, + cause, + }), + ), + ), + getByCommandId: (commandId) => + receipts.getByCommandId({ commandId }).pipe( + Effect.flatMap( + Option.match({ + onNone: () => Effect.succeed(Option.none()), + onSome: (receipt) => + receipt.aggregateKind !== "thread" + ? Effect.succeed(Option.none()) + : fromApplicationReceipt(receipt).pipe(Effect.map(Option.some)), + }), ), - getByCommandId: (commandId) => - receipts.getByCommandId({ commandId }).pipe( - Effect.flatMap( - Option.match({ - onNone: () => Effect.succeed(Option.none()), - onSome: (receipt) => - receipt.aggregateKind !== "thread" - ? Effect.succeed(Option.none()) - : fromApplicationReceipt(receipt).pipe(Effect.map(Option.some)), + Effect.mapError( + (cause) => + new CommandReceiptStoreReadError({ + commandId, + cause, }), - ), - Effect.mapError( - (cause) => - new CommandReceiptStoreReadError({ - commandId, - cause, - }), - ), ), - getProjectByCommandId: (commandId) => - receipts.getByCommandId({ commandId }).pipe( - Effect.flatMap((receipt) => - Option.isNone(receipt) || receipt.value.aggregateKind !== "project" - ? Effect.succeed(Option.none()) - : decodeProjectReceipt({ - commandId: receipt.value.commandId, - projectId: receipt.value.aggregateId, - commandType: receipt.value.commandType, - acceptedAt: receipt.value.acceptedAt, - resultSequence: receipt.value.resultSequence, - status: receipt.value.status, - error: receipt.value.error, - }).pipe(Effect.map(Option.some)), - ), - Effect.mapError((cause) => new CommandReceiptStoreReadError({ commandId, cause })), + ), + getProjectByCommandId: (commandId) => + receipts.getByCommandId({ commandId }).pipe( + Effect.flatMap((receipt) => + Option.isNone(receipt) || receipt.value.aggregateKind !== "project" + ? Effect.succeed(Option.none()) + : decodeProjectReceipt({ + commandId: receipt.value.commandId, + projectId: receipt.value.aggregateId, + commandType: receipt.value.commandType, + acceptedAt: receipt.value.acceptedAt, + resultSequence: receipt.value.resultSequence, + status: receipt.value.status, + error: receipt.value.error, + }).pipe(Effect.map(Option.some)), ), - } satisfies CommandReceiptStoreV2Shape); - }), - ); + Effect.mapError((cause) => new CommandReceiptStoreReadError({ commandId, cause })), + ), + } satisfies CommandReceiptStoreV2Shape); + }), +); export const layer = baseLayer.pipe(Layer.provide(OrchestrationCommandReceiptRepositoryLive)); diff --git a/apps/server/src/orchestration-v2/ContextHandoffService.test.ts b/apps/server/src/orchestration-v2/ContextHandoffService.test.ts index 872f4eadbd56..2418527507ce 100644 --- a/apps/server/src/orchestration-v2/ContextHandoffService.test.ts +++ b/apps/server/src/orchestration-v2/ContextHandoffService.test.ts @@ -12,14 +12,10 @@ import * as DateTime from "effect/DateTime"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; -import { - ContextHandoffServiceV2, - layer as contextHandoffServiceLayer, - providerMessageWithContextHandoff, -} from "./ContextHandoffService.ts"; -import { layer as idAllocatorLayer } from "./IdAllocator.ts"; +import * as ContextHandoffService from "./ContextHandoffService.ts"; +import * as IdAllocator from "./IdAllocator.ts"; -const TestLayer = contextHandoffServiceLayer.pipe(Layer.provide(idAllocatorLayer)); +const TestLayer = ContextHandoffService.layer.pipe(Layer.provide(IdAllocator.layer)); function importedItem( input: @@ -74,7 +70,7 @@ function importedItem( it.layer(TestLayer)("ContextHandoffService legacy import", (it) => { it.effect("prepares imported history for the first native v2 turn", () => Effect.gen(function* () { - const service = yield* ContextHandoffServiceV2; + const service = yield* ContextHandoffService.ContextHandoffServiceV2; const handoff = yield* service.prepareLegacyImport({ threadId: ThreadId.make("thread:legacy-context"), targetRunId: RunId.make("run:first-v2"), @@ -96,7 +92,7 @@ it.layer(TestLayer)("ContextHandoffService legacy import", (it) => { assert.deepStrictEqual(handoff.fromProviderThreadIds, []); assert.include(handoff.summaryText, "What did we decide?"); assert.include(handoff.summaryText, "keep the migration lightweight"); - const providerMessage = providerMessageWithContextHandoff({ + const providerMessage = ContextHandoffService.providerMessageWithContextHandoff({ handoff, userText: "Continue from there.", }); @@ -107,7 +103,7 @@ it.layer(TestLayer)("ContextHandoffService legacy import", (it) => { it.effect("preserves role attribution when truncating imported history", () => Effect.gen(function* () { - const service = yield* ContextHandoffServiceV2; + const service = yield* ContextHandoffService.ContextHandoffServiceV2; const handoff = yield* service.prepareLegacyImport({ threadId: ThreadId.make("thread:legacy-context"), targetRunId: RunId.make("run:first-v2"), @@ -132,7 +128,7 @@ it.layer(TestLayer)("ContextHandoffService legacy import", (it) => { it.effect("retains the newest oversized import even when it has no whitespace", () => Effect.gen(function* () { - const service = yield* ContextHandoffServiceV2; + const service = yield* ContextHandoffService.ContextHandoffServiceV2; const handoff = yield* service.prepareLegacyImport({ threadId: ThreadId.make("thread:legacy-context"), targetRunId: RunId.make("run:first-v2"), diff --git a/apps/server/src/orchestration-v2/ContextHandoffService.ts b/apps/server/src/orchestration-v2/ContextHandoffService.ts index be03bf049f4c..226c2119d3ea 100644 --- a/apps/server/src/orchestration-v2/ContextHandoffService.ts +++ b/apps/server/src/orchestration-v2/ContextHandoffService.ts @@ -21,7 +21,7 @@ import { renderHistory, selectHistory, } from "./ContextHandoffBudget.ts"; -import { IdAllocatorV2 } from "./IdAllocator.ts"; +import * as IdAllocator from "./IdAllocator.ts"; export class ContextHandoffPrepareError extends Schema.TaggedError()( "ContextHandoffPrepareError", @@ -228,7 +228,7 @@ function providerMessageWithContextHandoffs(input: { const makeContextHandoffService = Effect.fn("orchestrationV2.ContextHandoffService.layer")( function* () { - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const tokenCap = yield* handoffTokenCapConfig.pipe( Effect.orElseSucceed(() => DEFAULT_HANDOFF_TOKEN_CAP), ); @@ -494,7 +494,5 @@ const makeContextHandoffService = Effect.fn("orchestrationV2.ContextHandoffServi }, ); -export const layer: Layer.Layer = Layer.effect( - ContextHandoffServiceV2, - makeContextHandoffService(), -); +export const layer: Layer.Layer = + Layer.effect(ContextHandoffServiceV2, makeContextHandoffService()); diff --git a/apps/server/src/orchestration-v2/CursorOrchestratorV2.live.test.ts b/apps/server/src/orchestration-v2/CursorOrchestratorV2.live.test.ts index bdeafa018e25..61e746551fe4 100644 --- a/apps/server/src/orchestration-v2/CursorOrchestratorV2.live.test.ts +++ b/apps/server/src/orchestration-v2/CursorOrchestratorV2.live.test.ts @@ -1,5 +1,5 @@ import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; -import { SourceControlProviderRegistry } from "../sourceControl/SourceControlProviderRegistry.ts"; +import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it } from "@effect/vitest"; import { @@ -20,32 +20,31 @@ import { describe } from "vite-plus/test"; import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; import * as HostPowerMonitor from "../background/HostPowerMonitor.ts"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; -import { AntigravityInstallation } from "../provider/AntigravityInstallation.ts"; -import { CodexInstallation } from "../provider/CodexInstallation.ts"; -import { ServerEnvironmentIdentity } from "../environment/ServerEnvironment.ts"; +import * as AntigravityInstallation from "../provider/AntigravityInstallation.ts"; +import * as CodexInstallation from "../provider/CodexInstallation.ts"; +import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; import * as ModelManifest from "../provider/ModelManifest.ts"; import { ProviderInstanceRegistryHydrationLive } from "../provider/Layers/ProviderInstanceRegistryHydration.ts"; -import { - NoOpProviderEventLoggers, - ProviderEventLoggers, -} from "../provider/Layers/ProviderEventLoggers.ts"; -import { OpenCodeRuntimeLive } from "../provider/opencodeRuntime.ts"; +import * as ProviderEventLoggers from "../provider/Layers/ProviderEventLoggers.ts"; +import * as OpenCodeRuntime from "../provider/opencodeRuntime.ts"; import * as OpenCodeServerLedger from "../provider/OpenCodeServerLedger.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; +import * as ServerSettings from "../serverSettings.ts"; import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; import * as VcsProcess from "../vcs/VcsProcess.ts"; -import { OrchestratorV2 } from "./Orchestrator.ts"; +import * as Orchestrator from "./Orchestrator.ts"; import { worktreeRepairDependenciesTestLayer } from "./ProviderTurnStartService.testkit.ts"; -import { runDaemonWithOptions as runEffectWorkerDaemonWithOptions } from "./EffectWorker.ts"; +import * as EffectWorker from "./EffectWorker.ts"; import { OrchestrationV2LayerLive } from "./runtimeLayer.ts"; -import { layer as mcpSessionRegistryTestLayer } from "../mcp/McpSessionRegistry.testkit.ts"; +import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; import { CURSOR_MODEL_SELECTION, SUBAGENT_PROMPT } from "./testkit/fixtures/shared.ts"; const PlatformTestLayer = Layer.merge( NodeServices.layer, - Layer.mock(SourceControlProviderRegistry)({ resolveLink: () => Effect.die("unused title link") }), + Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ + resolveLink: () => Effect.die("unused title link"), + }), ); const serverConfigLayer = ServerConfig.layerTest(process.cwd(), { @@ -60,7 +59,7 @@ const vcsDriverRegistryLayer = VcsDriverRegistry.layer.pipe( const checkpointStoreLayer = CheckpointStore.layer.pipe(Layer.provide(vcsDriverRegistryLayer)); -const serverSettingsLayer = ServerSettingsService.layerTest({ +const serverSettingsLayer = ServerSettings.layerTest({ providers: { cursor: { enabled: true }, }, @@ -80,20 +79,25 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe ), NodeServices.layer, FetchHttpClient.layer, - OpenCodeRuntimeLive.pipe( + OpenCodeRuntime.OpenCodeRuntimeLive.pipe( Layer.provide(OpenCodeServerLedger.layerTest), Layer.provide(PlatformTestLayer), ), - Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers), + Layer.succeed( + ProviderEventLoggers.ProviderEventLoggers, + ProviderEventLoggers.NoOpProviderEventLoggers, + ), ModelManifest.layerTest, - AntigravityInstallation.layer.pipe( + AntigravityInstallation.AntigravityInstallation.layer.pipe( Layer.provide(serverConfigLayer.pipe(Layer.provide(PlatformTestLayer))), Layer.provide(FetchHttpClient.layer), Layer.provide(PlatformTestLayer), ), // The Codex driver now resolves managed ChatGPT installs; these runs never launch Codex. - Layer.mock(CodexInstallation)({ managedDirectory: "unused-managed-installation" }), - Layer.succeed(ServerEnvironmentIdentity, { + Layer.mock(CodexInstallation.CodexInstallation)({ + managedDirectory: "unused-managed-installation", + }), + Layer.succeed(ServerEnvironment.ServerEnvironmentIdentity, { getEnvironmentId: Effect.succeed( EnvironmentId.make("00000000-0000-4000-8000-000000000001"), ), @@ -104,7 +108,7 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe const liveLayer = OrchestrationV2LayerLive.pipe( Layer.provide(worktreeRepairDependenciesTestLayer), - Layer.provide(mcpSessionRegistryTestLayer), + Layer.provide(McpSessionRegistryTestkit.layer), Layer.provide(SqlitePersistenceMemory), Layer.provide(checkpointStoreLayer), Layer.provide(serverConfigLayer), @@ -118,7 +122,7 @@ const liveLayer = OrchestrationV2LayerLive.pipe( const waitForIdle = Effect.fn("CursorOrchestratorV2Live.waitForIdle")(function* ( threadId: ThreadId, ) { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; for (let attempt = 0; attempt < 600; attempt += 1) { const projection = yield* orchestrator.getThreadProjection(threadId); if ( @@ -141,8 +145,8 @@ describe.runIf(process.env.T3_CURSOR_LIVE_ORCHESTRATOR === "1")( "forks through portable context using real Cursor agents", () => Effect.gen(function* () { - yield* runEffectWorkerDaemonWithOptions({ concurrency: 2 }).pipe(Effect.forkScoped); - const orchestrator = yield* OrchestratorV2; + yield* EffectWorker.runDaemonWithOptions({ concurrency: 2 }).pipe(Effect.forkScoped); + const orchestrator = yield* Orchestrator.OrchestratorV2; const projectId = ProjectId.make("project:cursor-live-portable-fork"); const sourceThreadId = ThreadId.make("thread:cursor-live-portable-fork:source"); const targetThreadId = ThreadId.make("thread:cursor-live-portable-fork:target"); @@ -238,8 +242,8 @@ describe.runIf(process.env.T3_CURSOR_LIVE_ORCHESTRATOR === "1")( "runs a sandboxed thread after a full access thread in the same server", () => Effect.gen(function* () { - yield* runEffectWorkerDaemonWithOptions({ concurrency: 2 }).pipe(Effect.forkScoped); - const orchestrator = yield* OrchestratorV2; + yield* EffectWorker.runDaemonWithOptions({ concurrency: 2 }).pipe(Effect.forkScoped); + const orchestrator = yield* Orchestrator.OrchestratorV2; const projectId = ProjectId.make("project:cursor-live-sandbox-after-full-access"); const runThread = Effect.fn("CursorOrchestratorV2Live.runThread")(function* (input: { @@ -300,8 +304,8 @@ describe.runIf(process.env.T3_CURSOR_LIVE_ORCHESTRATOR === "1")( "spawns native subagents with child thread lineage", () => Effect.gen(function* () { - yield* runEffectWorkerDaemonWithOptions({ concurrency: 2 }).pipe(Effect.forkScoped); - const orchestrator = yield* OrchestratorV2; + yield* EffectWorker.runDaemonWithOptions({ concurrency: 2 }).pipe(Effect.forkScoped); + const orchestrator = yield* Orchestrator.OrchestratorV2; const projectId = ProjectId.make("project:cursor-live-subagent-lineage"); const sourceThreadId = ThreadId.make("thread:cursor-live-subagent-lineage"); diff --git a/apps/server/src/orchestration-v2/DelegatedCompletionDelivery.test.ts b/apps/server/src/orchestration-v2/DelegatedCompletionDelivery.test.ts index 74e0864369e5..a64dc21cec91 100644 --- a/apps/server/src/orchestration-v2/DelegatedCompletionDelivery.test.ts +++ b/apps/server/src/orchestration-v2/DelegatedCompletionDelivery.test.ts @@ -1,4 +1,4 @@ -import { SourceControlProviderRegistry } from "../sourceControl/SourceControlProviderRegistry.ts"; +import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it } from "@effect/vitest"; import { @@ -20,20 +20,20 @@ import * as Layer from "effect/Layer"; import * as Stream from "effect/Stream"; import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; -import { ServerConfig } from "../config.ts"; -import { layer as mcpSessionRegistryTestLayer } from "../mcp/McpSessionRegistry.testkit.ts"; +import * as ServerConfig from "../config.ts"; +import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; -import { ProjectEnrichmentService } from "../project/ProjectEnrichmentService.ts"; -import { ProjectService } from "../project/ProjectService.ts"; +import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; +import * as ProjectService from "../project/ProjectService.ts"; import type { ProviderInstance } from "../provider/ProviderDriver.ts"; -import { ProviderInstanceRegistry } from "../provider/Services/ProviderInstanceRegistry.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; +import * as ProviderInstanceRegistry from "../provider/Services/ProviderInstanceRegistry.ts"; +import * as ServerSettings from "../serverSettings.ts"; import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; import * as VcsProcess from "../vcs/VcsProcess.ts"; -import { WorkspacePaths } from "../workspace/WorkspacePaths.ts"; +import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; -import { EventSinkV2 } from "./EventSink.ts"; -import { OrchestratorV2 } from "./Orchestrator.ts"; +import * as EventSink from "./EventSink.ts"; +import * as Orchestrator from "./Orchestrator.ts"; import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; import { OrchestrationV2EventSinkLayerLive, @@ -44,7 +44,9 @@ import { worktreeRepairDependenciesTestLayer } from "./ProviderTurnStartService. const PlatformTestLayer = Layer.merge( NodeServices.layer, - Layer.mock(SourceControlProviderRegistry)({ resolveLink: () => Effect.die("unused title link") }), + Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ + resolveLink: () => Effect.die("unused title link"), + }), ); const ServerConfigLayer = ServerConfig.layerTest(process.cwd(), { @@ -89,25 +91,28 @@ const providerInstance = { textGeneration: {} as ProviderInstance["textGeneration"], } satisfies ProviderInstance; -const TestProviderInstanceRegistry = Layer.succeed(ProviderInstanceRegistry, { - getInstance: (instanceId) => - Effect.succeed(instanceId === providerInstance.instanceId ? providerInstance : undefined), - listInstances: Effect.succeed([providerInstance]), - listUnavailable: Effect.succeed([]), - streamChanges: Stream.empty, - subscribeChanges: Effect.never, -}); +const TestProviderInstanceRegistry = Layer.succeed( + ProviderInstanceRegistry.ProviderInstanceRegistry, + { + getInstance: (instanceId) => + Effect.succeed(instanceId === providerInstance.instanceId ? providerInstance : undefined), + listInstances: Effect.succeed([providerInstance]), + listUnavailable: Effect.succeed([]), + streamChanges: Stream.empty, + subscribeChanges: Effect.never, + }, +); const TestLayer = Layer.mergeAll(OrchestrationV2LayerLive, OrchestrationV2EventSinkLayerLive).pipe( Layer.provideMerge(ProjectServiceLayerLive), Layer.provide( - Layer.mock(WorkspacePaths)({ + Layer.mock(WorkspacePaths.WorkspacePaths)({ normalizeWorkspaceRoot: (workspaceRoot) => Effect.succeed(workspaceRoot), }), ), Layer.provide(worktreeRepairDependenciesTestLayer), Layer.provide( - Layer.succeed(ProjectEnrichmentService, { + Layer.succeed(ProjectEnrichmentService.ProjectEnrichmentService, { peek: () => Effect.succeed({ repositoryIdentity: null, @@ -125,11 +130,11 @@ const TestLayer = Layer.mergeAll(OrchestrationV2LayerLive, OrchestrationV2EventS subscribeChanges: Effect.never, }), ), - Layer.provide(mcpSessionRegistryTestLayer), + Layer.provide(McpSessionRegistryTestkit.layer), Layer.provide(SqlitePersistenceMemory), Layer.provide(CheckpointStoreTestLayer), Layer.provide(ServerConfigLayer), - Layer.provide(ServerSettingsService.layerTest()), + Layer.provide(ServerSettings.layerTest()), Layer.provide(TestProviderInstanceRegistry), Layer.provide(PlatformTestLayer), ); @@ -146,9 +151,9 @@ const seedParentWithTerminalTask = (input: { readonly now: DateTime.Utc; }) => Effect.gen(function* () { - const projects = yield* ProjectService; - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; + const projects = yield* ProjectService.ProjectService; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; const providerThreadId = ProviderThreadId.make( `provider-thread:${String(input.threadId).replace("thread:", "")}`, ); @@ -288,8 +293,8 @@ const seedParentWithTerminalTask = (input: { it.layer(TestLayer)("delegated completion delivery repairs", (it) => { it.effect("acceptance batches pending siblings without acknowledging their results", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const sink = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const sink = yield* EventSink.EventSinkV2; const now = yield* DateTime.now; const threadId = ThreadId.make("mailbox-batch"); const runId = RunId.make("mailbox-parent"); @@ -381,7 +386,7 @@ it.layer(TestLayer)("delegated completion delivery repairs", (it) => { it.effect("builds completion text and metadata from the same live cohort", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:delegated-delivery-live-cohort"); const projectId = ProjectId.make("project:delegated-delivery-live-cohort"); @@ -431,7 +436,7 @@ it.layer(TestLayer)("delegated completion delivery repairs", (it) => { it.effect("does not re-offer when wake-policy upgrades after delivered ownership settled", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:delegated-delivery-a1"); const projectId = ProjectId.make("project:delegated-delivery-a1"); @@ -496,7 +501,7 @@ it.layer(TestLayer)("delegated completion delivery repairs", (it) => { "treats repeated acknowledge and dispose with distinct command IDs as successful no-ops", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:delegated-delivery-a2"); const projectId = ProjectId.make("project:delegated-delivery-a2"); diff --git a/apps/server/src/orchestration-v2/EffectWorker.test.ts b/apps/server/src/orchestration-v2/EffectWorker.test.ts index c082ed35b6eb..af2667a8cf8e 100644 --- a/apps/server/src/orchestration-v2/EffectWorker.test.ts +++ b/apps/server/src/orchestration-v2/EffectWorker.test.ts @@ -19,25 +19,16 @@ import * as Queue from "effect/Queue"; import * as Ref from "effect/Ref"; import * as TestClock from "effect/testing/TestClock"; -import { CheckpointRollbackServiceV2 } from "./CheckpointRollbackService.ts"; -import { EffectOutboxError, EffectOutboxV2, type OrchestrationEffectV2 } from "./EffectOutbox.ts"; -import { - executorLayer, - isNonRetryableProviderTurnControlFailure, - layerWithOptions as effectWorkerLayerWithOptions, - OrchestrationEffectExecutionError, - OrchestrationEffectExecutorV2, - OrchestrationEffectWorkerError, - OrchestrationEffectWorkerV2, - runDaemonWithOptions, -} from "./EffectWorker.ts"; -import { RunFinalizationService } from "./RunFinalizationService.ts"; -import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts"; -import { ProviderTurnControlServiceV2 } from "./ProviderTurnControlService.ts"; -import { ProviderTurnStartError, ProviderTurnStartServiceV2 } from "./ProviderTurnStartService.ts"; -import { RuntimeRequestServiceV2 } from "./RuntimeRequestService.ts"; -import { ThreadTitleRegenerationService } from "./ThreadTitleRegenerationService.ts"; -import { ThreadManagementService } from "./ThreadManagementService.ts"; +import * as CheckpointRollbackService from "./CheckpointRollbackService.ts"; +import * as EffectOutbox from "./EffectOutbox.ts"; +import * as EffectWorker from "./EffectWorker.ts"; +import * as RunFinalizationService from "./RunFinalizationService.ts"; +import * as ProviderSessionManager from "./ProviderSessionManager.ts"; +import * as ProviderTurnControlService from "./ProviderTurnControlService.ts"; +import * as ProviderTurnStartService from "./ProviderTurnStartService.ts"; +import * as RuntimeRequestService from "./RuntimeRequestService.ts"; +import * as ThreadTitleRegenerationService from "./ThreadTitleRegenerationService.ts"; +import * as ThreadManagementService from "./ThreadManagementService.ts"; import * as ServerSettings from "../serverSettings.ts"; const threadId = ThreadId.make("thread:effect-worker-restart"); @@ -54,11 +45,11 @@ function restartEffect( now: DateTime.Utc, sessionTransition: NonNullable< Extract< - OrchestrationEffectV2["request"], + EffectOutbox.OrchestrationEffectV2["request"], { readonly type: "provider-turn.restart" } >["sessionTransition"] >, -): OrchestrationEffectV2 { +): EffectOutbox.OrchestrationEffectV2 { const timestamp = DateTime.formatIso(now); return { id: `effect:restart:${sessionTransition.type}`, @@ -92,8 +83,8 @@ function makeExecutorLayer(input: { const record = (event: string) => Ref.update(input.events, (events) => [...events, event]); const dependencies = Layer.mergeAll( Layer.succeed( - ProviderTurnControlServiceV2, - ProviderTurnControlServiceV2.of({ + ProviderTurnControlService.ProviderTurnControlServiceV2, + ProviderTurnControlService.ProviderTurnControlServiceV2.of({ interrupt: () => Effect.void, steer: () => Effect.void, interruptAndAwaitTerminal: (request) => @@ -105,8 +96,8 @@ function makeExecutorLayer(input: { }), ), Layer.succeed( - ProviderSessionManagerV2, - ProviderSessionManagerV2.of({ + ProviderSessionManager.ProviderSessionManagerV2, + ProviderSessionManager.ProviderSessionManagerV2.of({ shutdown: Effect.void, open: () => Effect.die("unused open"), get: () => Effect.succeed(Option.none()), @@ -117,8 +108,8 @@ function makeExecutorLayer(input: { }), ), Layer.succeed( - ProviderTurnStartServiceV2, - ProviderTurnStartServiceV2.of({ + ProviderTurnStartService.ProviderTurnStartServiceV2, + ProviderTurnStartService.ProviderTurnStartServiceV2.of({ start: () => Effect.gen(function* () { yield* record("start"); @@ -126,7 +117,7 @@ function makeExecutorLayer(input: { input.failFirstStart !== undefined && (yield* Ref.getAndSet(input.failFirstStart, false)) ) { - return yield* new ProviderTurnStartError({ + return yield* new ProviderTurnStartService.ProviderTurnStartError({ runId, cause: "simulated first start failure", }); @@ -135,27 +126,29 @@ function makeExecutorLayer(input: { }), ), Layer.succeed( - RunFinalizationService, - RunFinalizationService.of({ finalize: () => Effect.void }), + RunFinalizationService.RunFinalizationService, + RunFinalizationService.RunFinalizationService.of({ finalize: () => Effect.void }), ), Layer.succeed( - CheckpointRollbackServiceV2, - CheckpointRollbackServiceV2.of({ execute: () => Effect.void }), + CheckpointRollbackService.CheckpointRollbackServiceV2, + CheckpointRollbackService.CheckpointRollbackServiceV2.of({ execute: () => Effect.void }), ), Layer.succeed( - RuntimeRequestServiceV2, - RuntimeRequestServiceV2.of({ respond: () => Effect.void }), + RuntimeRequestService.RuntimeRequestServiceV2, + RuntimeRequestService.RuntimeRequestServiceV2.of({ respond: () => Effect.void }), ), Layer.succeed( - ThreadTitleRegenerationService, - ThreadTitleRegenerationService.of({ execute: () => Effect.void }), + ThreadTitleRegenerationService.ThreadTitleRegenerationService, + ThreadTitleRegenerationService.ThreadTitleRegenerationService.of({ + execute: () => Effect.void, + }), ), ); - return executorLayer.pipe( + return EffectWorker.executorLayer.pipe( Layer.provide( Layer.mergeAll( dependencies, - Layer.mock(ThreadManagementService)({}), + Layer.mock(ThreadManagementService.ThreadManagementService)({}), ServerSettings.layerTest(), ), ), @@ -164,32 +157,32 @@ function makeExecutorLayer(input: { it("does not retry pure interrupt races where the turn is already gone", () => { assert.isTrue( - isNonRetryableProviderTurnControlFailure( + EffectWorker.isNonRetryableProviderTurnControlFailure( "provider-turn.interrupt", "ProviderAdapterInterruptError: ... ACP provider turn provider-turn:x is not active", ), ); assert.isTrue( - isNonRetryableProviderTurnControlFailure( + EffectWorker.isNonRetryableProviderTurnControlFailure( "provider-turn.interrupt", "Provider session provider-session:x is not active.", ), ); // Restart is compound (interrupt + detach + start). Do not swallow start failures. assert.isFalse( - isNonRetryableProviderTurnControlFailure( + EffectWorker.isNonRetryableProviderTurnControlFailure( "provider-turn.restart", "Provider session provider-session:x is not active.", ), ); assert.isFalse( - isNonRetryableProviderTurnControlFailure( + EffectWorker.isNonRetryableProviderTurnControlFailure( "provider-turn.start", "Provider session provider-session:x is not active.", ), ); assert.isFalse( - isNonRetryableProviderTurnControlFailure( + EffectWorker.isNonRetryableProviderTurnControlFailure( "provider-turn.interrupt", "ACP hard teardown failed unexpectedly; the session is poisoned", ), @@ -201,7 +194,7 @@ it.effect("requeues a claim when a pre-execution worker check fails", () => const now = DateTime.formatIso(yield* DateTime.now); const effectId = "effect:worker-pre-execution-failure"; const workerId = "worker-pre-execution-failure"; - const claimedEffect: OrchestrationEffectV2 = { + const claimedEffect: EffectOutbox.OrchestrationEffectV2 = { id: effectId, commandId: CommandId.make("command:worker-pre-execution-failure"), threadId: ThreadId.make("thread:worker-pre-execution-failure"), @@ -225,11 +218,11 @@ it.effect("requeues a claim when a pre-execution worker check fails", () => }> >([]); const executionCount = yield* Ref.make(0); - const outboxLayer = Layer.mock(EffectOutboxV2)({ + const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ claimNext: () => Effect.succeed(Option.some(claimedEffect)), get: () => Effect.fail( - new EffectOutboxError({ + new EffectOutbox.EffectOutboxError({ operation: "get", effectId, cause: "simulated cancellation-state read failure", @@ -239,16 +232,16 @@ it.effect("requeues a claim when a pre-execution worker check fails", () => Ref.update(retries, (existing) => [...existing, input]).pipe(Effect.as(true)), }); const executorLayer = Layer.succeed( - OrchestrationEffectExecutorV2, - OrchestrationEffectExecutorV2.of({ + EffectWorker.OrchestrationEffectExecutorV2, + EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Ref.update(executionCount, (count) => count + 1), }), ); - const workerLayer = effectWorkerLayerWithOptions({ workerId }).pipe( + const workerLayer = EffectWorker.layerWithOptions({ workerId }).pipe( Layer.provide(Layer.merge(outboxLayer, executorLayer)), ); - const exit = yield* OrchestrationEffectWorkerV2.pipe( + const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( Effect.flatMap((worker) => worker.runOnce), Effect.provide(workerLayer), Effect.exit, @@ -273,7 +266,7 @@ it.effect("arms cancellation before the durable pre-execution check", () => const now = DateTime.formatIso(yield* DateTime.now); const effectId = "effect:worker-cancellation-registration-race"; const workerId = "worker-cancellation-registration-race"; - const claimedEffect: OrchestrationEffectV2 = { + const claimedEffect: EffectOutbox.OrchestrationEffectV2 = { id: effectId, commandId: CommandId.make("command:worker-cancellation-registration-race"), threadId: ThreadId.make("thread:worker-cancellation-registration-race"), @@ -292,7 +285,7 @@ it.effect("arms cancellation before the durable pre-execution check", () => let cancellationArmed = false; const executionCount = yield* Ref.make(0); const settlementCount = yield* Ref.make(0); - const outboxLayer = Layer.mock(EffectOutboxV2)({ + const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ claimNext: () => Effect.succeed(Option.some(claimedEffect)), awaitCancellation: () => { cancellationArmed = true; @@ -312,17 +305,17 @@ it.effect("arms cancellation before the durable pre-execution check", () => succeed: () => Ref.update(settlementCount, (count) => count + 1).pipe(Effect.as(true)), }); const executorLayer = Layer.succeed( - OrchestrationEffectExecutorV2, - OrchestrationEffectExecutorV2.of({ + EffectWorker.OrchestrationEffectExecutorV2, + EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Effect.yieldNow.pipe(Effect.andThen(Ref.update(executionCount, (count) => count + 1))), }), ); - const workerLayer = effectWorkerLayerWithOptions({ workerId }).pipe( + const workerLayer = EffectWorker.layerWithOptions({ workerId }).pipe( Layer.provide(Layer.merge(outboxLayer, executorLayer)), ); - const exit = yield* OrchestrationEffectWorkerV2.pipe( + const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( Effect.flatMap((worker) => worker.runOnce), Effect.provide(workerLayer), Effect.exit, @@ -341,7 +334,7 @@ it.effect("terminalizes a process-bound claim when success settlement fails", () const now = DateTime.formatIso(yield* DateTime.now); const effectId = "effect:worker-process-bound-settlement-failure"; const workerId = "worker-process-bound-settlement-failure"; - const claimedEffect: OrchestrationEffectV2 = { + const claimedEffect: EffectOutbox.OrchestrationEffectV2 = { id: effectId, commandId: CommandId.make("command:worker-process-bound-settlement-failure"), threadId: ThreadId.make("thread:worker-process-bound-settlement-failure"), @@ -362,14 +355,14 @@ it.effect("terminalizes a process-bound claim when success settlement fails", () const retries = yield* Ref.make(0); const terminalErrors = yield* Ref.make>([]); const executionCount = yield* Ref.make(0); - const outboxLayer = Layer.mock(EffectOutboxV2)({ + const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ claimNext: () => Effect.succeed(Option.some(claimedEffect)), get: () => Effect.succeed(Option.some(claimedEffect)), awaitCancellation: () => Effect.never, clearCancellation: () => Effect.void, succeed: () => Effect.fail( - new EffectOutboxError({ + new EffectOutbox.EffectOutboxError({ operation: "succeed", effectId, cause: "simulated success settlement failure", @@ -380,16 +373,16 @@ it.effect("terminalizes a process-bound claim when success settlement fails", () Ref.update(terminalErrors, (existing) => [...existing, error]).pipe(Effect.as(true)), }); const executorLayer = Layer.succeed( - OrchestrationEffectExecutorV2, - OrchestrationEffectExecutorV2.of({ + EffectWorker.OrchestrationEffectExecutorV2, + EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Ref.update(executionCount, (count) => count + 1), }), ); - const workerLayer = effectWorkerLayerWithOptions({ workerId }).pipe( + const workerLayer = EffectWorker.layerWithOptions({ workerId }).pipe( Layer.provide(Layer.merge(outboxLayer, executorLayer)), ); - const exit = yield* OrchestrationEffectWorkerV2.pipe( + const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( Effect.flatMap((worker) => worker.runOnce), Effect.provide(workerLayer), Effect.exit, @@ -410,7 +403,7 @@ it.effect("requeues a replay-safe claim when success settlement fails", () => const now = DateTime.formatIso(yield* DateTime.now); const effectId = "effect:worker-replay-safe-settlement-failure"; const workerId = "worker-replay-safe-settlement-failure"; - const claimedEffect: OrchestrationEffectV2 = { + const claimedEffect: EffectOutbox.OrchestrationEffectV2 = { id: effectId, commandId: CommandId.make("command:worker-replay-safe-settlement-failure"), threadId: ThreadId.make("thread:worker-replay-safe-settlement-failure"), @@ -427,14 +420,14 @@ it.effect("requeues a replay-safe claim when success settlement fails", () => }; const retries = yield* Ref.make(0); const terminalizations = yield* Ref.make(0); - const outboxLayer = Layer.mock(EffectOutboxV2)({ + const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ claimNext: () => Effect.succeed(Option.some(claimedEffect)), get: () => Effect.succeed(Option.some(claimedEffect)), awaitCancellation: () => Effect.never, clearCancellation: () => Effect.void, succeed: () => Effect.fail( - new EffectOutboxError({ + new EffectOutbox.EffectOutboxError({ operation: "succeed", effectId, cause: "simulated replay-safe settlement failure", @@ -444,14 +437,14 @@ it.effect("requeues a replay-safe claim when success settlement fails", () => fail: () => Ref.update(terminalizations, (count) => count + 1).pipe(Effect.as(true)), }); const executorLayer = Layer.succeed( - OrchestrationEffectExecutorV2, - OrchestrationEffectExecutorV2.of({ execute: () => Effect.void }), + EffectWorker.OrchestrationEffectExecutorV2, + EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Effect.void }), ); - const workerLayer = effectWorkerLayerWithOptions({ workerId }).pipe( + const workerLayer = EffectWorker.layerWithOptions({ workerId }).pipe( Layer.provide(Layer.merge(outboxLayer, executorLayer)), ); - const exit = yield* OrchestrationEffectWorkerV2.pipe( + const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( Effect.flatMap((worker) => worker.runOnce), Effect.provide(workerLayer), Effect.exit, @@ -468,7 +461,7 @@ it.effect("keeps a process-bound executor failure retryable when retry settlemen const now = DateTime.formatIso(yield* DateTime.now); const effectId = "effect:worker-process-bound-retry-settlement-failure"; const workerId = "worker-process-bound-retry-settlement-failure"; - const claimedEffect: OrchestrationEffectV2 = { + const claimedEffect: EffectOutbox.OrchestrationEffectV2 = { id: effectId, commandId: CommandId.make("command:worker-process-bound-retry-settlement-failure"), threadId: ThreadId.make("thread:worker-process-bound-retry-settlement-failure"), @@ -488,7 +481,7 @@ it.effect("keeps a process-bound executor failure retryable when retry settlemen }; const retryAttempts = yield* Ref.make(0); const terminalizations = yield* Ref.make(0); - const outboxLayer = Layer.mock(EffectOutboxV2)({ + const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ claimNext: () => Effect.succeed(Option.some(claimedEffect)), get: () => Effect.succeed(Option.some(claimedEffect)), awaitCancellation: () => Effect.never, @@ -498,7 +491,7 @@ it.effect("keeps a process-bound executor failure retryable when retry settlemen Effect.flatMap((attempt) => attempt === 1 ? Effect.fail( - new EffectOutboxError({ + new EffectOutbox.EffectOutboxError({ operation: "retry", effectId, cause: "simulated retry settlement failure", @@ -510,11 +503,11 @@ it.effect("keeps a process-bound executor failure retryable when retry settlemen fail: () => Ref.update(terminalizations, (count) => count + 1).pipe(Effect.as(true)), }); const executorLayer = Layer.succeed( - OrchestrationEffectExecutorV2, - OrchestrationEffectExecutorV2.of({ + EffectWorker.OrchestrationEffectExecutorV2, + EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Effect.fail( - new OrchestrationEffectExecutionError({ + new EffectWorker.OrchestrationEffectExecutionError({ effectId, effectType: claimedEffect.request.type, cause: "simulated provider execution failure", @@ -522,11 +515,11 @@ it.effect("keeps a process-bound executor failure retryable when retry settlemen ), }), ); - const workerLayer = effectWorkerLayerWithOptions({ workerId }).pipe( + const workerLayer = EffectWorker.layerWithOptions({ workerId }).pipe( Layer.provide(Layer.merge(outboxLayer, executorLayer)), ); - const exit = yield* OrchestrationEffectWorkerV2.pipe( + const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( Effect.flatMap((worker) => worker.runOnce), Effect.provide(workerLayer), Effect.exit, @@ -543,7 +536,7 @@ it.effect("keeps a max-attempt replay-safe failure terminal when fail settlement const now = DateTime.formatIso(yield* DateTime.now); const effectId = "effect:worker-replay-safe-terminal-settlement-failure"; const workerId = "worker-replay-safe-terminal-settlement-failure"; - const claimedEffect: OrchestrationEffectV2 = { + const claimedEffect: EffectOutbox.OrchestrationEffectV2 = { id: effectId, commandId: CommandId.make("command:worker-replay-safe-terminal-settlement-failure"), threadId: ThreadId.make("thread:worker-replay-safe-terminal-settlement-failure"), @@ -560,7 +553,7 @@ it.effect("keeps a max-attempt replay-safe failure terminal when fail settlement }; const failAttempts = yield* Ref.make(0); const retries = yield* Ref.make(0); - const outboxLayer = Layer.mock(EffectOutboxV2)({ + const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ claimNext: () => Effect.succeed(Option.some(claimedEffect)), get: () => Effect.succeed(Option.some(claimedEffect)), awaitCancellation: () => Effect.never, @@ -570,7 +563,7 @@ it.effect("keeps a max-attempt replay-safe failure terminal when fail settlement Effect.flatMap((attempt) => attempt === 1 ? Effect.fail( - new EffectOutboxError({ + new EffectOutbox.EffectOutboxError({ operation: "fail", effectId, cause: "simulated terminal settlement failure", @@ -582,11 +575,11 @@ it.effect("keeps a max-attempt replay-safe failure terminal when fail settlement retry: () => Ref.update(retries, (count) => count + 1).pipe(Effect.as(true)), }); const executorLayer = Layer.succeed( - OrchestrationEffectExecutorV2, - OrchestrationEffectExecutorV2.of({ + EffectWorker.OrchestrationEffectExecutorV2, + EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Effect.fail( - new OrchestrationEffectExecutionError({ + new EffectWorker.OrchestrationEffectExecutionError({ effectId, effectType: claimedEffect.request.type, cause: "simulated terminal cleanup failure", @@ -594,11 +587,11 @@ it.effect("keeps a max-attempt replay-safe failure terminal when fail settlement ), }), ); - const workerLayer = effectWorkerLayerWithOptions({ workerId, maxAttempts: 5 }).pipe( + const workerLayer = EffectWorker.layerWithOptions({ workerId, maxAttempts: 5 }).pipe( Layer.provide(Layer.merge(outboxLayer, executorLayer)), ); - const exit = yield* OrchestrationEffectWorkerV2.pipe( + const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( Effect.flatMap((worker) => worker.runOnce), Effect.provide(workerLayer), Effect.exit, @@ -618,7 +611,7 @@ it.effect("uses durable deadlines, notifications, and a slow liveness poll", () const nextClaimableAt = yield* Ref.make>( Option.some(DateTime.add(now, { milliseconds: 100 })), ); - const worker = OrchestrationEffectWorkerV2.of({ + const worker = EffectWorker.OrchestrationEffectWorkerV2.of({ awaitWork: Queue.take(available), runRecoveryOnce: Effect.succeed(false), runOnce: Effect.gen(function* () { @@ -638,10 +631,13 @@ it.effect("uses durable deadlines, notifications, and a slow liveness poll", () } }); - yield* runDaemonWithOptions({ + yield* EffectWorker.runDaemonWithOptions({ concurrency: 1, livenessPollIntervalMs: 1_000, - }).pipe(Effect.provideService(OrchestrationEffectWorkerV2, worker), Effect.forkScoped); + }).pipe( + Effect.provideService(EffectWorker.OrchestrationEffectWorkerV2, worker), + Effect.forkScoped, + ); yield* awaitAttempts(1); yield* TestClock.adjust("99 millis"); @@ -666,12 +662,12 @@ it.effect("does not hot-loop when a claim fails", () => Effect.gen(function* () { const attempts = yield* Ref.make(0); const now = yield* DateTime.now; - const worker = OrchestrationEffectWorkerV2.of({ + const worker = EffectWorker.OrchestrationEffectWorkerV2.of({ awaitWork: Effect.never, runRecoveryOnce: Effect.succeed(false), runOnce: Ref.update(attempts, (count) => count + 1).pipe( Effect.andThen( - new OrchestrationEffectWorkerError({ + new EffectWorker.OrchestrationEffectWorkerError({ operation: "claim", cause: "simulated database failure", }), @@ -681,10 +677,13 @@ it.effect("does not hot-loop when a claim fails", () => drain: () => Effect.succeed(0), }); - yield* runDaemonWithOptions({ + yield* EffectWorker.runDaemonWithOptions({ concurrency: 1, livenessPollIntervalMs: 1_000, - }).pipe(Effect.provideService(OrchestrationEffectWorkerV2, worker), Effect.forkScoped); + }).pipe( + Effect.provideService(EffectWorker.OrchestrationEffectWorkerV2, worker), + Effect.forkScoped, + ); while ((yield* Ref.get(attempts)) < 1) yield* Effect.yieldNow; yield* TestClock.adjust("999 millis"); @@ -698,7 +697,7 @@ it.effect("backs off briefly when a due deadline loses a claim race", () => Effect.gen(function* () { const attempts = yield* Ref.make(0); const now = yield* DateTime.now; - const worker = OrchestrationEffectWorkerV2.of({ + const worker = EffectWorker.OrchestrationEffectWorkerV2.of({ awaitWork: Effect.never, runRecoveryOnce: Effect.succeed(false), runOnce: Ref.update(attempts, (count) => count + 1).pipe(Effect.as(false)), @@ -706,10 +705,13 @@ it.effect("backs off briefly when a due deadline loses a claim race", () => drain: () => Effect.succeed(0), }); - yield* runDaemonWithOptions({ + yield* EffectWorker.runDaemonWithOptions({ concurrency: 1, livenessPollIntervalMs: 1_000, - }).pipe(Effect.provideService(OrchestrationEffectWorkerV2, worker), Effect.forkScoped); + }).pipe( + Effect.provideService(EffectWorker.OrchestrationEffectWorkerV2, worker), + Effect.forkScoped, + ); while ((yield* Ref.get(attempts)) < 1) yield* Effect.yieldNow; yield* TestClock.adjust("24 millis"); @@ -731,13 +733,13 @@ it.effect("safely retries after replacement cleanup succeeds and start fails", ( const layer = makeExecutorLayer({ events, failFirstStart }); const first = yield* Effect.gen(function* () { - const executor = yield* OrchestrationEffectExecutorV2; + const executor = yield* EffectWorker.OrchestrationEffectExecutorV2; return yield* Effect.exit(executor.execute(effect)); }).pipe(Effect.provide(layer)); assert.isTrue(Exit.isFailure(first)); yield* Effect.gen(function* () { - const executor = yield* OrchestrationEffectExecutorV2; + const executor = yield* EffectWorker.OrchestrationEffectExecutorV2; yield* executor.execute(effect); }).pipe(Effect.provide(layer)); diff --git a/apps/server/src/orchestration-v2/EffectWorker.ts b/apps/server/src/orchestration-v2/EffectWorker.ts index eeedafd28722..3d94f1baa8df 100644 --- a/apps/server/src/orchestration-v2/EffectWorker.ts +++ b/apps/server/src/orchestration-v2/EffectWorker.ts @@ -16,21 +16,17 @@ import { orchestrationEffectClaimsTotal, orchestrationEffectQueueWait, } from "../observability/Metrics.ts"; -import { RunFinalizationService } from "./RunFinalizationService.ts"; -import { ResourceCleanupService } from "./ResourceCleanupService.ts"; -import { - EffectOutboxV2, - REPLAY_SAFE_EFFECT_TYPES_AFTER_PROCESS_LOSS, - type OrchestrationEffectV2, -} from "./EffectOutbox.ts"; +import * as RunFinalizationService from "./RunFinalizationService.ts"; +import * as ResourceCleanupService from "./ResourceCleanupService.ts"; +import * as EffectOutbox from "./EffectOutbox.ts"; import * as CheckpointRollbackService from "./CheckpointRollbackService.ts"; -import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts"; -import { ProviderTurnControlServiceV2 } from "./ProviderTurnControlService.ts"; -import { ProviderTurnStartServiceV2 } from "./ProviderTurnStartService.ts"; -import { RuntimeRequestServiceV2 } from "./RuntimeRequestService.ts"; -import { ThreadTitleRegenerationService } from "./ThreadTitleRegenerationService.ts"; -import { ThreadManagementService } from "./ThreadManagementService.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; +import * as ProviderSessionManager from "./ProviderSessionManager.ts"; +import * as ProviderTurnControlService from "./ProviderTurnControlService.ts"; +import * as ProviderTurnStartService from "./ProviderTurnStartService.ts"; +import * as RuntimeRequestService from "./RuntimeRequestService.ts"; +import * as ThreadTitleRegenerationService from "./ThreadTitleRegenerationService.ts"; +import * as ThreadManagementService from "./ThreadManagementService.ts"; +import * as ServerSettings from "../serverSettings.ts"; import { continueRestartedRun } from "./RestartContinuation.ts"; export class OrchestrationEffectExecutionError extends Schema.TaggedError()( @@ -72,7 +68,7 @@ export interface OrchestrationEffectExecutorV2Shape { * failure, so a step can fail and try again instead of settling the run. */ readonly execute: ( - effect: OrchestrationEffectV2, + effect: EffectOutbox.OrchestrationEffectV2, options?: { readonly willRetry: boolean }, ) => Effect.Effect; } @@ -85,28 +81,29 @@ export class OrchestrationEffectExecutorV2 extends Context.Service< export const executorLayer: Layer.Layer< OrchestrationEffectExecutorV2, never, - | ProviderSessionManagerV2 - | RunFinalizationService + | ProviderSessionManager.ProviderSessionManagerV2 + | RunFinalizationService.RunFinalizationService | CheckpointRollbackService.CheckpointRollbackServiceV2 - | ProviderTurnControlServiceV2 - | ProviderTurnStartServiceV2 - | RuntimeRequestServiceV2 - | ThreadTitleRegenerationService - | ThreadManagementService - | ServerSettingsService + | ProviderTurnControlService.ProviderTurnControlServiceV2 + | ProviderTurnStartService.ProviderTurnStartServiceV2 + | RuntimeRequestService.RuntimeRequestServiceV2 + | ThreadTitleRegenerationService.ThreadTitleRegenerationService + | ThreadManagementService.ThreadManagementService + | ServerSettings.ServerSettingsService > = Layer.effect( OrchestrationEffectExecutorV2, Effect.gen(function* () { - const runFinalization = yield* RunFinalizationService; - const resourceCleanup = yield* ResourceCleanupService; + const runFinalization = yield* RunFinalizationService.RunFinalizationService; + const resourceCleanup = yield* ResourceCleanupService.ResourceCleanupService; const checkpointRollback = yield* CheckpointRollbackService.CheckpointRollbackServiceV2; - const providerSessions = yield* ProviderSessionManagerV2; - const providerTurnControl = yield* ProviderTurnControlServiceV2; - const providerTurnStart = yield* ProviderTurnStartServiceV2; - const runtimeRequests = yield* RuntimeRequestServiceV2; - const threadTitleRegeneration = yield* ThreadTitleRegenerationService; - const threads = yield* ThreadManagementService; - const settings = yield* ServerSettingsService; + const providerSessions = yield* ProviderSessionManager.ProviderSessionManagerV2; + const providerTurnControl = yield* ProviderTurnControlService.ProviderTurnControlServiceV2; + const providerTurnStart = yield* ProviderTurnStartService.ProviderTurnStartServiceV2; + const runtimeRequests = yield* RuntimeRequestService.RuntimeRequestServiceV2; + const threadTitleRegeneration = + yield* ThreadTitleRegenerationService.ThreadTitleRegenerationService; + const threads = yield* ThreadManagementService.ThreadManagementService; + const settings = yield* ServerSettings.ServerSettingsService; return OrchestrationEffectExecutorV2.of({ execute: (effect, options) => { const willRetry = options?.willRetry ?? false; @@ -116,8 +113,8 @@ export const executorLayer: Layer.Layer< threadId: effect.threadId, sourceRunId: effect.request.sourceRunId, }).pipe( - Effect.provideService(ThreadManagementService, threads), - Effect.provideService(ServerSettingsService, settings), + Effect.provideService(ThreadManagementService.ThreadManagementService, threads), + Effect.provideService(ServerSettings.ServerSettingsService, settings), Effect.mapError( (cause) => new OrchestrationEffectExecutionError({ @@ -482,12 +479,12 @@ export const layerWithOptions = ( ): Layer.Layer< OrchestrationEffectWorkerV2, never, - EffectOutboxV2 | OrchestrationEffectExecutorV2 + EffectOutbox.EffectOutboxV2 | OrchestrationEffectExecutorV2 > => Layer.effect( OrchestrationEffectWorkerV2, Effect.gen(function* () { - const outbox = yield* EffectOutboxV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const executor = yield* OrchestrationEffectExecutorV2; const workerId = options.workerId ?? `orchestration-v2:${process.pid}`; const leaseDurationMs = Math.max(1, options.leaseDurationMs ?? 30_000); @@ -501,7 +498,10 @@ export const layerWithOptions = ( }), ), ); - const requeueClaim = (effect: OrchestrationEffectV2, cause: Cause.Cause) => + const requeueClaim = ( + effect: EffectOutbox.OrchestrationEffectV2, + cause: Cause.Cause, + ) => Cause.hasInterruptsOnly(cause) ? Effect.void : outbox @@ -531,7 +531,10 @@ export const layerWithOptions = ( }), ), ); - const terminalizeClaim = (effect: OrchestrationEffectV2, cause: Cause.Cause) => { + const terminalizeClaim = ( + effect: EffectOutbox.OrchestrationEffectV2, + cause: Cause.Cause, + ) => { if (Cause.hasInterruptsOnly(cause)) return Effect.void; return outbox .fail({ @@ -567,10 +570,10 @@ export const layerWithOptions = ( ); }; const recoverPostSuccessSettlement = ( - effect: OrchestrationEffectV2, + effect: EffectOutbox.OrchestrationEffectV2, cause: Cause.Cause, ) => - REPLAY_SAFE_EFFECT_TYPES_AFTER_PROCESS_LOSS.some( + EffectOutbox.REPLAY_SAFE_EFFECT_TYPES_AFTER_PROCESS_LOSS.some( (effectType) => effectType === effect.request.type, ) ? requeueClaim(effect, cause) diff --git a/apps/server/src/orchestration-v2/EventSink.ts b/apps/server/src/orchestration-v2/EventSink.ts index 851bff04c955..c61ce6ba8b7b 100644 --- a/apps/server/src/orchestration-v2/EventSink.ts +++ b/apps/server/src/orchestration-v2/EventSink.ts @@ -25,28 +25,12 @@ import { replayAndBufferProjectedLiveEvents } from "./LiveStreamBudget.ts"; import type { UnsequencedProjectEvent } from "../persistence/Services/OrchestrationEventStore.ts"; import { projectDomainEventForWire } from "./WireProjection.ts"; -import { - CommandReceiptStoreV2, - type CommandReceiptV2, - type ProjectCommandReceiptV2, - layer as commandReceiptStoreLayer, -} from "./CommandReceiptStore.ts"; -import { - EffectOutboxV2, - type OrchestrationEffectRequestV2, - type PendingOrchestrationEffectV2, - layer as effectOutboxLayer, -} from "./EffectOutbox.ts"; -import { EventStoreV2 } from "./EventStore.ts"; -import { - ORCHESTRATION_V2_PROJECTION_SCHEMA_VERSION, - ProjectionStoreV2, -} from "./ProjectionStore.ts"; +import * as CommandReceiptStore from "./CommandReceiptStore.ts"; +import * as EffectOutbox from "./EffectOutbox.ts"; +import * as EventStore from "./EventStore.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; import * as ProjectStore from "./ProjectStore.ts"; -import { - TurnItemPositionStoreV2, - layer as turnItemPositionStoreLayer, -} from "./TurnItemPositionStore.ts"; +import * as TurnItemPositionStore from "./TurnItemPositionStore.ts"; /** * ERRORS @@ -95,7 +79,7 @@ export interface EventSinkV2Shape { readonly guardPendingUserInputCancellations?: boolean; readonly commandId?: CommandId; readonly events: ReadonlyArray; - readonly effects: ReadonlyArray; + readonly effects: ReadonlyArray; }) => Effect.Effect, EventSinkV2Error>; readonly writeIfRunCurrent: (input: { readonly guardPendingUserInputCancellations?: boolean; @@ -139,14 +123,14 @@ export interface EventSinkV2Shape { readonly commandType: string; readonly acceptedAt: DateTime.Utc; readonly events: ReadonlyArray; - readonly effects: ReadonlyArray; + readonly effects: ReadonlyArray; readonly cancelUnsettledEffects?: { - readonly effectTypes: ReadonlyArray; + readonly effectTypes: ReadonlyArray; readonly reason: string; }; }) => Effect.Effect< { - readonly receipt: CommandReceiptV2; + readonly receipt: CommandReceiptStore.CommandReceiptV2; readonly storedEvents: ReadonlyArray; readonly committed: boolean; readonly cancelledEffectCount: number; @@ -159,7 +143,7 @@ export interface EventSinkV2Shape { readonly commandType: string; readonly rejectedAt: DateTime.Utc; readonly error: string; - }) => Effect.Effect; + }) => Effect.Effect; /** * Append a project event, fold it into its row and record the receipt in one * transaction. A reused command id commits nothing and returns its receipt. @@ -171,7 +155,7 @@ export interface EventSinkV2Shape { readonly acceptedAt: DateTime.Utc; readonly event: UnsequencedProjectEvent; }) => Effect.Effect< - { readonly receipt: ProjectCommandReceiptV2; readonly committed: boolean }, + { readonly receipt: CommandReceiptStore.ProjectCommandReceiptV2; readonly committed: boolean }, EventSinkV2Error >; /** Record a rejected project command, or return the receipt its command id already has. */ @@ -181,7 +165,7 @@ export interface EventSinkV2Shape { readonly commandType: string; readonly rejectedAt: DateTime.Utc; readonly error: string; - }) => Effect.Effect; + }) => Effect.Effect; readonly stream: (input?: { readonly threadId?: ThreadId; readonly afterSequence?: number; @@ -208,23 +192,23 @@ export class EventSinkV2 extends Context.Service( const baseLayer: Layer.Layer< EventSinkV2, never, - | CommandReceiptStoreV2 - | EffectOutboxV2 - | EventStoreV2 - | ProjectionStoreV2 + | CommandReceiptStore.CommandReceiptStoreV2 + | EffectOutbox.EffectOutboxV2 + | EventStore.EventStoreV2 + | ProjectionStore.ProjectionStoreV2 | ProjectStore.ProjectStoreV2 | SqlClient.SqlClient - | TurnItemPositionStoreV2 + | TurnItemPositionStore.TurnItemPositionStoreV2 > = Layer.effect( EventSinkV2, Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - const commandReceipts = yield* CommandReceiptStoreV2; - const effectOutbox = yield* EffectOutboxV2; - const eventStore = yield* EventStoreV2; - const projectionStore = yield* ProjectionStoreV2; + const commandReceipts = yield* CommandReceiptStore.CommandReceiptStoreV2; + const effectOutbox = yield* EffectOutbox.EffectOutboxV2; + const eventStore = yield* EventStore.EventStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const projectStore = yield* ProjectStore.ProjectStoreV2; - const turnItemPositions = yield* TurnItemPositionStoreV2; + const turnItemPositions = yield* TurnItemPositionStore.TurnItemPositionStoreV2; const liveEvents = yield* PubSub.unbounded(); const liveEventsByType = new Map< OrchestrationV2DomainEvent["type"], @@ -326,7 +310,7 @@ const baseLayer: Layer.Layer< ) VALUES ( 'thread-projections', - ${ORCHESTRATION_V2_PROJECTION_SCHEMA_VERSION}, + ${ProjectionStore.ORCHESTRATION_V2_PROJECTION_SCHEMA_VERSION}, ${sequence}, ${now} ) @@ -535,7 +519,7 @@ const baseLayer: Layer.Layer< } yield* applyStoredEvents(storedEvents); yield* effectOutbox.enqueue(input.effects); - const receipt: CommandReceiptV2 = { + const receipt: CommandReceiptStore.CommandReceiptV2 = { commandId: input.commandId, threadId: input.threadId, commandType: input.commandType, @@ -577,7 +561,7 @@ const baseLayer: Layer.Layer< return yield* sql.withTransaction( Effect.gen(function* () { const sequence = yield* eventStore.latestSequence({ threadId: input.threadId }); - const receipt: CommandReceiptV2 = { + const receipt: CommandReceiptStore.CommandReceiptV2 = { commandId: input.commandId, threadId: input.threadId, commandType: input.commandType, @@ -611,7 +595,7 @@ const baseLayer: Layer.Layer< function* (input: Parameters[0]) { const result = yield* sql.withTransaction( Effect.gen(function* () { - const reserved: ProjectCommandReceiptV2 = { + const reserved: CommandReceiptStore.ProjectCommandReceiptV2 = { commandId: input.commandId, projectId: input.projectId, commandType: input.commandType, @@ -642,7 +626,7 @@ const baseLayer: Layer.Layer< )(function* (input: Parameters[0]) { return yield* sql.withTransaction( Effect.gen(function* () { - const receipt: ProjectCommandReceiptV2 = { + const receipt: CommandReceiptStore.ProjectCommandReceiptV2 = { commandId: input.commandId, projectId: input.projectId, commandType: input.commandType, @@ -867,14 +851,14 @@ export const layerFromStores = baseLayer; export const layer: Layer.Layer< EventSinkV2, never, - EventStoreV2 | ProjectionStoreV2 | SqlClient.SqlClient + EventStore.EventStoreV2 | ProjectionStore.ProjectionStoreV2 | SqlClient.SqlClient > = baseLayer.pipe( Layer.provide( Layer.mergeAll( - commandReceiptStoreLayer, - effectOutboxLayer, + CommandReceiptStore.layer, + EffectOutbox.layer, ProjectStore.layer, - turnItemPositionStoreLayer, + TurnItemPositionStore.layer, ), ), ); diff --git a/apps/server/src/orchestration-v2/EventStore.ts b/apps/server/src/orchestration-v2/EventStore.ts index a646152c6b80..da3ccca2f267 100644 --- a/apps/server/src/orchestration-v2/EventStore.ts +++ b/apps/server/src/orchestration-v2/EventStore.ts @@ -14,10 +14,7 @@ import * as Stream from "effect/Stream"; import type * as SqlClient from "effect/unstable/sql/SqlClient"; import { OrchestrationEventStoreLive } from "../persistence/Layers/OrchestrationEventStore.ts"; -import { - OrchestrationEventStore, - type UnsequencedProjectEvent, -} from "../persistence/Services/OrchestrationEventStore.ts"; +import * as OrchestrationEventStore from "../persistence/Services/OrchestrationEventStore.ts"; export class EventStoreAppendEventsError extends Schema.TaggedError()( "EventStoreAppendEventsError", @@ -58,7 +55,7 @@ export interface EventStoreV2Shape { readonly events: ReadonlyArray; }) => Effect.Effect, EventStoreV2Error>; readonly appendProjectEvent: ( - event: UnsequencedProjectEvent, + event: OrchestrationEventStore.UnsequencedProjectEvent, ) => Effect.Effect; readonly read: (input?: { readonly afterSequence?: number; @@ -82,74 +79,75 @@ export class EventStoreV2 extends Context.Service = Layer.effect( - EventStoreV2, - Effect.gen(function* () { - const applicationEvents = yield* OrchestrationEventStore; - - const read: EventStoreV2Shape["read"] = (input) => - applicationEvents - .readAgentEvents({ - ...(input?.afterSequence === undefined ? {} : { afterSequence: input.afterSequence }), - ...(input?.throughSequence === undefined - ? {} - : { throughSequence: input.throughSequence }), - ...(input?.threadId === undefined ? {} : { threadId: input.threadId }), - ...(input?.eventType === undefined ? {} : { eventType: input.eventType }), - ...(input?.limit === undefined ? {} : { limit: input.limit }), - }) - .pipe( - Stream.mapError( - (cause) => - new EventStoreReadEventsError({ - ...(input?.afterSequence === undefined - ? {} - : { afterSequence: input.afterSequence }), - ...(input?.threadId === undefined ? {} : { threadId: input.threadId }), - cause, - }), - ), - ); +const baseLayer: Layer.Layer = + Layer.effect( + EventStoreV2, + Effect.gen(function* () { + const applicationEvents = yield* OrchestrationEventStore.OrchestrationEventStore; - return EventStoreV2.of({ - append: (input) => - applicationEvents.appendAgentEvents(input).pipe( - Effect.mapError( - (cause) => - new EventStoreAppendEventsError({ - eventCount: input.events.length, - cause, - }), - ), - ), - appendProjectEvent: (event) => + const read: EventStoreV2Shape["read"] = (input) => applicationEvents - .appendProjectEvent(event) + .readAgentEvents({ + ...(input?.afterSequence === undefined ? {} : { afterSequence: input.afterSequence }), + ...(input?.throughSequence === undefined + ? {} + : { throughSequence: input.throughSequence }), + ...(input?.threadId === undefined ? {} : { threadId: input.threadId }), + ...(input?.eventType === undefined ? {} : { eventType: input.eventType }), + ...(input?.limit === undefined ? {} : { limit: input.limit }), + }) .pipe( - Effect.mapError((cause) => new EventStoreAppendEventsError({ eventCount: 1, cause })), + Stream.mapError( + (cause) => + new EventStoreReadEventsError({ + ...(input?.afterSequence === undefined + ? {} + : { afterSequence: input.afterSequence }), + ...(input?.threadId === undefined ? {} : { threadId: input.threadId }), + cause, + }), + ), + ); + + return EventStoreV2.of({ + append: (input) => + applicationEvents.appendAgentEvents(input).pipe( + Effect.mapError( + (cause) => + new EventStoreAppendEventsError({ + eventCount: input.events.length, + cause, + }), + ), ), - read, - readByCommandId: ({ commandId }) => - applicationEvents - .readAgentEvents({ commandId }) - .pipe(Stream.mapError((cause) => new EventStoreReadEventsError({ cause }))), - latestSequence: (input) => - applicationEvents.latestAgentSequence(input?.threadId).pipe( - Effect.mapError( - (cause) => - new EventStoreReadEventsError({ - ...(input?.threadId === undefined ? {} : { threadId: input.threadId }), - cause, - }), + appendProjectEvent: (event) => + applicationEvents + .appendProjectEvent(event) + .pipe( + Effect.mapError((cause) => new EventStoreAppendEventsError({ eventCount: 1, cause })), + ), + read, + readByCommandId: ({ commandId }) => + applicationEvents + .readAgentEvents({ commandId }) + .pipe(Stream.mapError((cause) => new EventStoreReadEventsError({ cause }))), + latestSequence: (input) => + applicationEvents.latestAgentSequence(input?.threadId).pipe( + Effect.mapError( + (cause) => + new EventStoreReadEventsError({ + ...(input?.threadId === undefined ? {} : { threadId: input.threadId }), + cause, + }), + ), ), + latestApplicationSequence: applicationEvents.latestApplicationSequence.pipe( + Effect.mapError((cause) => new EventStoreReadEventsError({ cause })), ), - latestApplicationSequence: applicationEvents.latestApplicationSequence.pipe( - Effect.mapError((cause) => new EventStoreReadEventsError({ cause })), - ), - publishCommitted: applicationEvents.publishCommitted, - }); - }), -); + publishCommitted: applicationEvents.publishCommitted, + }); + }), + ); export const layer: Layer.Layer = baseLayer.pipe( Layer.provide(OrchestrationEventStoreLive), diff --git a/apps/server/src/orchestration-v2/FoundationPersistence.test.ts b/apps/server/src/orchestration-v2/FoundationPersistence.test.ts index 376bb7b69b61..b7f6e642cffe 100644 --- a/apps/server/src/orchestration-v2/FoundationPersistence.test.ts +++ b/apps/server/src/orchestration-v2/FoundationPersistence.test.ts @@ -43,36 +43,27 @@ import * as Statement from "effect/unstable/sql/Statement"; import { LIVE_STREAM_MAX_ITEMS, LiveStreamBufferError } from "./LiveStreamBudget.ts"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; -import { CommandReceiptStoreV2, layer as commandReceiptStoreLayer } from "./CommandReceiptStore.ts"; -import { EffectOutboxV2, layer as effectOutboxLayer } from "./EffectOutbox.ts"; -import { - layerWithOptions as effectWorkerLayerWithOptions, - OrchestrationEffectExecutionError, - OrchestrationEffectExecutorV2, - OrchestrationEffectWorkerV2, - runDaemonWithOptions as runEffectWorkerDaemonWithOptions, -} from "./EffectWorker.ts"; -import { EventSinkV2, layer as eventSinkLayer } from "./EventSink.ts"; -import { EventStoreReadEventsError, EventStoreV2, layer as eventStoreLayer } from "./EventStore.ts"; -import { layer as idAllocatorLayer } from "./IdAllocator.ts"; -import { - ProjectionMaintenanceV2, - layer as projectionMaintenanceLayer, -} from "./ProjectionMaintenance.ts"; -import { ProjectionStoreV2, layer as projectionStoreLayer } from "./ProjectionStore.ts"; +import * as CommandReceiptStore from "./CommandReceiptStore.ts"; +import * as EffectOutbox from "./EffectOutbox.ts"; +import * as EffectWorker from "./EffectWorker.ts"; +import * as EventSink from "./EventSink.ts"; +import * as EventStore from "./EventStore.ts"; +import * as IdAllocator from "./IdAllocator.ts"; +import * as ProjectionMaintenance from "./ProjectionMaintenance.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; import * as ProviderRuntimeRecovery from "./ProviderRuntimeRecoveryService.ts"; const isLiveStreamBufferError = Schema.is(LiveStreamBufferError); const encodeJson = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown)); const databaseLayer = SqlitePersistenceMemory; -const eventStoreProvided = eventStoreLayer.pipe(Layer.provideMerge(databaseLayer)); -const projectionStoreProvided = projectionStoreLayer.pipe(Layer.provideMerge(databaseLayer)); +const eventStoreProvided = EventStore.layer.pipe(Layer.provideMerge(databaseLayer)); +const projectionStoreProvided = ProjectionStore.layer.pipe(Layer.provideMerge(databaseLayer)); const storesProvided = Layer.mergeAll(databaseLayer, eventStoreProvided, projectionStoreProvided); -const eventSinkProvided = eventSinkLayer.pipe(Layer.provide(storesProvided)); -const effectOutboxProvided = effectOutboxLayer.pipe(Layer.provide(databaseLayer)); -const commandReceiptStoreProvided = commandReceiptStoreLayer.pipe(Layer.provide(databaseLayer)); -const projectionMaintenanceProvided = projectionMaintenanceLayer.pipe( +const eventSinkProvided = EventSink.layer.pipe(Layer.provide(storesProvided)); +const effectOutboxProvided = EffectOutbox.layer.pipe(Layer.provide(databaseLayer)); +const commandReceiptStoreProvided = CommandReceiptStore.layer.pipe(Layer.provide(databaseLayer)); +const projectionMaintenanceProvided = ProjectionMaintenance.layer.pipe( Layer.provide(storesProvided), ); const TestLayer = Layer.mergeAll( @@ -80,7 +71,7 @@ const TestLayer = Layer.mergeAll( eventSinkProvided, effectOutboxProvided, commandReceiptStoreProvided, - idAllocatorLayer, + IdAllocator.layer, projectionMaintenanceProvided, ); @@ -138,8 +129,8 @@ function threadCreatedEvent(input: { it.effect("rebuilds event history one bounded page at a time", () => Effect.gen(function* () { - const eventStore = yield* EventStoreV2; - const projectionStore = yield* ProjectionStoreV2; + const eventStore = yield* EventStore.EventStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:foundation-paged-rebuild"); const thread = makeThread(threadId, now); @@ -160,20 +151,20 @@ it.effect("rebuilds event history one bounded page at a time", () => let failAfterFirstPage = false; const appliedAtRead: Array = []; const observedStores = Layer.mergeAll( - Layer.succeed(EventStoreV2, { + Layer.succeed(EventStore.EventStoreV2, { ...eventStore, read: (input) => Stream.suspend(() => { appliedAtRead.push(applied); if (failAfterFirstPage && applied >= 500) { return Stream.fail( - new EventStoreReadEventsError({ afterSequence: input?.afterSequence }), + new EventStore.EventStoreReadEventsError({ afterSequence: input?.afterSequence }), ); } return eventStore.read(input); }), }), - Layer.succeed(ProjectionStoreV2, { + Layer.succeed(ProjectionStore.ProjectionStoreV2, { ...projectionStore, apply: (event) => projectionStore.apply(event).pipe( @@ -186,10 +177,10 @@ it.effect("rebuilds event history one bounded page at a time", () => }), ); const rebuild = Effect.gen(function* () { - const maintenance = yield* ProjectionMaintenanceV2; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; return yield* maintenance.rebuild; }).pipe( - Effect.provide(Layer.fresh(projectionMaintenanceLayer.pipe(Layer.provide(observedStores)))), + Effect.provide(Layer.fresh(ProjectionMaintenance.layer.pipe(Layer.provide(observedStores)))), ); const rebuilt = yield* rebuild; assert.isTrue(rebuilt.valid); @@ -208,7 +199,7 @@ it.effect("rebuilds event history one bounded page at a time", () => (yield* projectionStore.getThreadProjection(threadId)).thread.title, "Rebuilt update 1004", ); - const maintenance = yield* ProjectionMaintenanceV2; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; assert.isTrue((yield* maintenance.verify).valid); }).pipe(Effect.provide(TestLayer)), ); @@ -216,7 +207,7 @@ it.effect("rebuilds event history one bounded page at a time", () => it.effect("verifies thread membership using only the thread-created partial index", () => Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - const maintenance = yield* ProjectionMaintenanceV2; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; const now = DateTime.formatIso(yield* DateTime.now); yield* sql` WITH RECURSIVE history(n) AS ( @@ -263,7 +254,7 @@ it.effect("keeps other database work runnable while discovering compaction candi Effect.scoped( Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - const maintenance = yield* ProjectionMaintenanceV2; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; const now = DateTime.formatIso(yield* DateTime.now); yield* sql` WITH RECURSIVE history(n) AS ( @@ -349,9 +340,9 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("projects oversized tool bodies before both replay and live RPC retention", () => Effect.scoped( Effect.gen(function* () { - const sink = yield* EventSinkV2; - const store = yield* EventStoreV2; - const projections = yield* ProjectionStoreV2; + const sink = yield* EventSink.EventSinkV2; + const store = yield* EventStore.EventStoreV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const thread = makeThread(ThreadId.make("thread:large-stream-body"), now); const output = { @@ -446,7 +437,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect(`bounds live events while the V2 ${phase} query is blocked`, () => Effect.scoped( Effect.gen(function* () { - const sink = yield* EventSinkV2; + const sink = yield* EventSink.EventSinkV2; const now = yield* DateTime.now; const thread = makeThread(ThreadId.make(`thread:blocked-${phase}`), now); const [created] = yield* sink.write({ @@ -503,7 +494,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { () => Effect.scoped( Effect.gen(function* () { - const sink = yield* EventSinkV2; + const sink = yield* EventSink.EventSinkV2; const now = yield* DateTime.now; const thread = makeThread(ThreadId.make("thread:internal-stream-burst"), now); const [created] = yield* sink.write({ @@ -553,7 +544,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("filters worker replay and live queues without losing matching events", () => Effect.scoped( Effect.gen(function* () { - const sink = yield* EventSinkV2; + const sink = yield* EventSink.EventSinkV2; const sql = yield* SqlClient.SqlClient; const now = yield* DateTime.now; const thread = makeThread(ThreadId.make("thread:filtered-worker"), now); @@ -647,7 +638,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("paginates catch-up beyond the event-store read limit", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; + const eventSink = yield* EventSink.EventSinkV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:foundation-large-catch-up"); const thread = makeThread(threadId, now); @@ -684,7 +675,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("does not lose or duplicate events while transitioning from catch-up to live", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; + const eventSink = yield* EventSink.EventSinkV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:foundation-stream-race"); const thread = makeThread(threadId, now); @@ -720,9 +711,9 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("replays shared provider-session payloads across every bound thread", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const projectionStore = yield* ProjectionStoreV2; - const maintenance = yield* ProjectionMaintenanceV2; + const eventSink = yield* EventSink.EventSinkV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; const now = yield* DateTime.now; const firstThreadId = ThreadId.make("thread:foundation-shared-session:first"); const secondThreadId = ThreadId.make("thread:foundation-shared-session:second"); @@ -785,10 +776,10 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("compacts superseded state events, imported v1 events, and legacy receipts", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const maintenance = yield* ProjectionMaintenanceV2; + const eventSink = yield* EventSink.EventSinkV2; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; const sql = yield* SqlClient.SqlClient; - const projections = yield* ProjectionStoreV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const nowIso = DateTime.formatIso(now); const threadId = ThreadId.make("thread:foundation-compact"); @@ -989,9 +980,9 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("verifies and rebuilds projections with cross-thread subagent relations", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const projectionStore = yield* ProjectionStoreV2; - const maintenance = yield* ProjectionMaintenanceV2; + const eventSink = yield* EventSink.EventSinkV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; const sql = yield* SqlClient.SqlClient; const now = yield* DateTime.now; const parentThreadId = ThreadId.make("thread:foundation-cross-thread:parent"); @@ -1171,10 +1162,10 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { "rolls back events, projections, receipts, and effects after a projection failure", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const eventStore = yield* EventStoreV2; - const receipts = yield* CommandReceiptStoreV2; - const outbox = yield* EffectOutboxV2; + const eventSink = yield* EventSink.EventSinkV2; + const eventStore = yield* EventStore.EventStoreV2; + const receipts = yield* CommandReceiptStore.CommandReceiptStoreV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const sql = yield* SqlClient.SqlClient; const now = yield* DateTime.now; const commandId = CommandId.make("command:foundation-atomic-failure"); @@ -1247,8 +1238,8 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("replays every command event across bounded persistence pages", () => Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - const eventStore = yield* EventStoreV2; - const eventSink = yield* EventSinkV2; + const eventStore = yield* EventStore.EventStoreV2; + const eventSink = yield* EventSink.EventSinkV2; const now = yield* DateTime.now; const occurredAt = DateTime.formatIso(now); const commandId = CommandId.make("command:paged-command-replay"); @@ -1406,8 +1397,8 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("keeps one durable effect across command retries and executes it after recovery", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const outbox = yield* EffectOutboxV2; + const eventSink = yield* EventSink.EventSinkV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const now = yield* DateTime.now; const commandId = CommandId.make("command:foundation-effect-recovery"); const threadId = ThreadId.make("thread:foundation-effect-recovery"); @@ -1452,16 +1443,18 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { const executionCount = yield* Ref.make(0); const executorLayer = Layer.succeed( - OrchestrationEffectExecutorV2, - OrchestrationEffectExecutorV2.of({ + EffectWorker.OrchestrationEffectExecutorV2, + EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Ref.update(executionCount, (count) => count + 1), }), ); - const workerLayer = effectWorkerLayerWithOptions({ workerId: "recovery-worker" }).pipe( - Layer.provide(Layer.merge(Layer.succeed(EffectOutboxV2, outbox), executorLayer)), + const workerLayer = EffectWorker.layerWithOptions({ workerId: "recovery-worker" }).pipe( + Layer.provide( + Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), executorLayer), + ), ); yield* Effect.gen(function* () { - const worker = yield* OrchestrationEffectWorkerV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; assert.isTrue(yield* worker.runOnce); assert.isFalse(yield* worker.runOnce); }).pipe(Effect.provide(workerLayer)); @@ -1477,8 +1470,8 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("does not wake claimers for effects from an idempotent command retry", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const outbox = yield* EffectOutboxV2; + const eventSink = yield* EventSink.EventSinkV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const now = yield* DateTime.now; const commandId = CommandId.make("command:foundation-idempotent-wakeup"); const threadId = ThreadId.make("thread:foundation-idempotent-wakeup"); @@ -1516,8 +1509,8 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("does not publish a stale provider start after an interrupt wins", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:foundation-stale-provider-start"); const runId = RunId.make("run:foundation-stale-provider-start"); @@ -1621,8 +1614,8 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("guards post-terminal provider-thread writes by attempt and run ordinal", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:foundation-provider-thread-owner"); const runId = RunId.make("run:foundation-provider-thread-owner"); @@ -1844,7 +1837,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("interrupts a running process-bound effect when it is cancelled", () => Effect.gen(function* () { - const outbox = yield* EffectOutboxV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const commandId = CommandId.make("command:foundation-cancel-running-effect"); const threadId = ThreadId.make("thread:foundation-cancel-running-effect"); const effectId = "effect:foundation-cancel-running-effect"; @@ -1863,8 +1856,8 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { ]); const executorLayer = Layer.succeed( - OrchestrationEffectExecutorV2, - OrchestrationEffectExecutorV2.of({ + EffectWorker.OrchestrationEffectExecutorV2, + EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Deferred.succeed(started, undefined).pipe( Effect.andThen(Effect.never), @@ -1874,12 +1867,16 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { ), }), ); - const workerLayer = effectWorkerLayerWithOptions({ + const workerLayer = EffectWorker.layerWithOptions({ workerId: "cancellation-worker", - }).pipe(Layer.provide(Layer.merge(Layer.succeed(EffectOutboxV2, outbox), executorLayer))); + }).pipe( + Layer.provide( + Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), executorLayer), + ), + ); yield* Effect.gen(function* () { - const worker = yield* OrchestrationEffectWorkerV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; const workerFiber = yield* worker.runOnce.pipe(Effect.forkChild); yield* Deferred.await(started); const cancelledEffectIds = yield* outbox.cancelUnsettled({ @@ -1920,7 +1917,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { completedAt: null, lastError: null, }; - const outboxLayer = Layer.mock(EffectOutboxV2)({ + const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ claimNext: () => Effect.succeed(Option.some(claimedEffect)), awaitCancellation: () => Effect.never, clearCancellation: () => Effect.void, @@ -1937,15 +1934,15 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { ), }); const executorLayer = Layer.succeed( - OrchestrationEffectExecutorV2, - OrchestrationEffectExecutorV2.of({ execute: () => Effect.void }), + EffectWorker.OrchestrationEffectExecutorV2, + EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Effect.void }), ); - const workerLayer = effectWorkerLayerWithOptions({ + const workerLayer = EffectWorker.layerWithOptions({ workerId: "settlement-race-worker", }).pipe(Layer.provide(Layer.merge(outboxLayer, executorLayer))); assert.isTrue( - yield* OrchestrationEffectWorkerV2.pipe( + yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( Effect.flatMap((worker) => worker.runOnce), Effect.provide(workerLayer), ), @@ -1975,7 +1972,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { lastError: null, }; const executionCount = yield* Ref.make(0); - const outboxLayer = Layer.mock(EffectOutboxV2)({ + const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ claimNext: () => Effect.succeed(Option.some(claimedEffect)), get: () => Effect.succeed( @@ -1991,17 +1988,17 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { awaitCancellation: () => Effect.never, }); const executorLayer = Layer.succeed( - OrchestrationEffectExecutorV2, - OrchestrationEffectExecutorV2.of({ + EffectWorker.OrchestrationEffectExecutorV2, + EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Ref.update(executionCount, (count) => count + 1), }), ); - const workerLayer = effectWorkerLayerWithOptions({ + const workerLayer = EffectWorker.layerWithOptions({ workerId: "claim-cancellation-worker", }).pipe(Layer.provide(Layer.merge(outboxLayer, executorLayer))); assert.isTrue( - yield* OrchestrationEffectWorkerV2.pipe( + yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( Effect.flatMap((worker) => worker.runOnce), Effect.provide(workerLayer), ), @@ -2012,7 +2009,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("allows only one worker to claim an available effect", () => Effect.gen(function* () { - const outbox = yield* EffectOutboxV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const commandId = CommandId.make("command:foundation-exclusive-claim"); yield* outbox.enqueue([ { @@ -2048,7 +2045,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("runs title generation beside critical work while serializing each lane", () => Effect.gen(function* () { - const outbox = yield* EffectOutboxV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const commandId = CommandId.make("command:foundation-title-effect-lane"); const threadId = ThreadId.make("thread:foundation-title-effect-lane"); const titleEffectId = "effect:foundation-title-effect-lane:a-title"; @@ -2153,7 +2150,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("ignores deadlines blocked by a running effect on the same thread", () => Effect.gen(function* () { - const outbox = yield* EffectOutboxV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const now = yield* DateTime.now; const future = DateTime.add(now, { seconds: 10 }); const commandId = CommandId.make("command:foundation-next-claimable"); @@ -2226,7 +2223,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("does not emit a SQL span for an empty safety claim", () => Effect.gen(function* () { - const outbox = yield* EffectOutboxV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const spans: Array = []; const tracer = Tracer.make({ span: (options) => { @@ -2251,7 +2248,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("wakes claimers when cancellation unblocks same-thread work", () => Effect.gen(function* () { - const outbox = yield* EffectOutboxV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const commandId = CommandId.make("command:foundation-cancellation-wakeup"); const threadId = ThreadId.make("thread:foundation-cancellation-wakeup"); yield* outbox.enqueue([ @@ -2308,20 +2305,20 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("executes a retry at its durable deadline instead of the liveness interval", () => Effect.gen(function* () { - const outbox = yield* EffectOutboxV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const effectId = "effect:foundation-durable-retry-deadline"; const commandId = CommandId.make("command:foundation-durable-retry-deadline"); const threadId = ThreadId.make("thread:foundation-durable-retry-deadline"); const executions = yield* Ref.make(0); const completed = yield* Deferred.make(); const executorLayer = Layer.succeed( - OrchestrationEffectExecutorV2, - OrchestrationEffectExecutorV2.of({ + EffectWorker.OrchestrationEffectExecutorV2, + EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Effect.gen(function* () { const attempt = yield* Ref.updateAndGet(executions, (count) => count + 1); if (attempt === 1) { - return yield* new OrchestrationEffectExecutionError({ + return yield* new EffectWorker.OrchestrationEffectExecutionError({ effectId, effectType: "terminal.cleanup", cause: "simulated retry", @@ -2331,12 +2328,16 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { }), }), ); - const workerLayer = effectWorkerLayerWithOptions({ + const workerLayer = EffectWorker.layerWithOptions({ workerId: "durable-retry-deadline-worker", - }).pipe(Layer.provide(Layer.merge(Layer.succeed(EffectOutboxV2, outbox), executorLayer))); + }).pipe( + Layer.provide( + Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), executorLayer), + ), + ); yield* Effect.gen(function* () { - yield* runEffectWorkerDaemonWithOptions({ + yield* EffectWorker.runDaemonWithOptions({ concurrency: 1, livenessPollIntervalMs: 30_000, }).pipe(Effect.forkScoped); @@ -2371,7 +2372,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("runs distinct threads concurrently while serializing effects within a thread", () => Effect.gen(function* () { - const outbox = yield* EffectOutboxV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const commandId = CommandId.make("command:foundation-concurrent-effects"); const threadA = ThreadId.make("thread:foundation-concurrent-effects:a"); const threadB = ThreadId.make("thread:foundation-concurrent-effects:b"); @@ -2390,8 +2391,8 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { [effectB1, { started: startedB1, release: releaseB1 }], ]); const executorLayer = Layer.succeed( - OrchestrationEffectExecutorV2, - OrchestrationEffectExecutorV2.of({ + EffectWorker.OrchestrationEffectExecutorV2, + EffectWorker.OrchestrationEffectExecutorV2.of({ execute: (effect) => { const gate = gates.get(effect.id); if (gate === undefined) return Effect.die(`Missing gate for ${effect.id}`); @@ -2401,12 +2402,16 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { }, }), ); - const workerLayer = effectWorkerLayerWithOptions({ + const workerLayer = EffectWorker.layerWithOptions({ workerId: "concurrency-worker", - }).pipe(Layer.provide(Layer.merge(Layer.succeed(EffectOutboxV2, outbox), executorLayer))); + }).pipe( + Layer.provide( + Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), executorLayer), + ), + ); yield* Effect.gen(function* () { - yield* runEffectWorkerDaemonWithOptions({ concurrency: 2 }).pipe(Effect.forkScoped); + yield* EffectWorker.runDaemonWithOptions({ concurrency: 2 }).pipe(Effect.forkScoped); // Let both slots reach the idle wait before work becomes available. yield* Effect.yieldNow; yield* Effect.yieldNow; @@ -2461,7 +2466,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("does not reclaim a running effect after its process-local lease expires", () => Effect.gen(function* () { - const outbox = yield* EffectOutboxV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const sql = yield* SqlClient.SqlClient; const commandId = CommandId.make("command:foundation-no-live-reclaim"); const threadId = ThreadId.make("thread:foundation-no-live-reclaim"); @@ -2486,8 +2491,8 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { ]); const executorLayer = Layer.succeed( - OrchestrationEffectExecutorV2, - OrchestrationEffectExecutorV2.of({ + EffectWorker.OrchestrationEffectExecutorV2, + EffectWorker.OrchestrationEffectExecutorV2.of({ execute: (effect) => Ref.update(executions, (current) => [...current, effect.id]).pipe( Effect.andThen( @@ -2500,13 +2505,17 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { ), }), ); - const workerLayer = effectWorkerLayerWithOptions({ + const workerLayer = EffectWorker.layerWithOptions({ workerId: "no-live-reclaim-worker", leaseDurationMs: 1, - }).pipe(Layer.provide(Layer.merge(Layer.succeed(EffectOutboxV2, outbox), executorLayer))); + }).pipe( + Layer.provide( + Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), executorLayer), + ), + ); yield* Effect.gen(function* () { - const worker = yield* OrchestrationEffectWorkerV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; const firstFiber = yield* worker.runOnce.pipe(Effect.forkChild); yield* Deferred.await(firstStarted); yield* sql` @@ -2530,7 +2539,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { "persists shutdown continuation intent through the real event sink without domain events", () => Effect.gen(function* () { - const outbox = yield* EffectOutboxV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:shutdown-prepare"); const runId = RunId.make("run:shutdown-prepare"); @@ -2574,11 +2583,11 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { Effect.provide( Layer.mergeAll( ServerSettings.layerTest({ continueThreadsAfterServerUpdate: true }), - Layer.mock(ProjectionStoreV2)({ + Layer.mock(ProjectionStore.ProjectionStoreV2)({ getRecoveryThreadIds: () => Effect.succeed([threadId]), getRuntimeRecoveryProjection: () => Effect.succeed(projection), }), - Layer.mock(OrchestrationEffectWorkerV2)({}), + Layer.mock(EffectWorker.OrchestrationEffectWorkerV2)({}), ), ), ); @@ -2604,7 +2613,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("leaves restart continuation pending until normal worker claims are enabled", () => Effect.gen(function* () { - const outbox = yield* EffectOutboxV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const threadId = ThreadId.make("thread:activation-restart"); const commandId = CommandId.make("command:activation-restart"); yield* outbox.enqueue([ @@ -2660,7 +2669,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { `retires live provider effects and requeues ${replayRequest.type} after process loss`, () => Effect.gen(function* () { - const outbox = yield* EffectOutboxV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const commandId = CommandId.make( `command:foundation-reclaim-running:${replayRequest.type}`, ); @@ -2717,9 +2726,9 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("atomically cancels stale runs and their process-bound effects", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const outbox = yield* EffectOutboxV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:foundation-process-loss"); const runId = RunId.make("run:foundation-process-loss"); @@ -2777,8 +2786,8 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { const recovery = yield* ProviderRuntimeRecovery.make.pipe( Effect.provide(ServerSettings.layerTest()), Effect.provideService( - OrchestrationEffectWorkerV2, - OrchestrationEffectWorkerV2.of({ + EffectWorker.OrchestrationEffectWorkerV2, + EffectWorker.OrchestrationEffectWorkerV2.of({ awaitWork: Effect.void, runRecoveryOnce: Effect.succeed(false), runOnce: Effect.succeed(false), @@ -2804,8 +2813,8 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("settles a native subagent's child thread when its provider process is gone", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const parentId = ThreadId.make("thread:foundation-native-subagent-parent"); const childId = ThreadId.make("thread:foundation-native-subagent-child"); @@ -2999,8 +3008,8 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { const recovery = yield* ProviderRuntimeRecovery.make.pipe( Effect.provide(ServerSettings.layerTest()), Effect.provideService( - OrchestrationEffectWorkerV2, - OrchestrationEffectWorkerV2.of({ + EffectWorker.OrchestrationEffectWorkerV2, + EffectWorker.OrchestrationEffectWorkerV2.of({ awaitWork: Effect.void, runRecoveryOnce: Effect.succeed(false), runOnce: Effect.succeed(false), @@ -3029,9 +3038,9 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.effect("allocates collision-free positions beyond 100 items and rebuilds equivalently", () => Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const projectionStore = yield* ProjectionStoreV2; - const maintenance = yield* ProjectionMaintenanceV2; + const eventSink = yield* EventSink.EventSinkV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; const sql = yield* SqlClient.SqlClient; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:foundation-many-items"); @@ -3174,11 +3183,11 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { it.live("keeps claiming new work after repeated idle periods", () => Effect.gen(function* () { - const outbox = yield* EffectOutboxV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const completed = new Map>(); const executorLayer = Layer.succeed( - OrchestrationEffectExecutorV2, - OrchestrationEffectExecutorV2.of({ + EffectWorker.OrchestrationEffectExecutorV2, + EffectWorker.OrchestrationEffectExecutorV2.of({ execute: (effect) => { const completion = completed.get(effect.id); return completion === undefined @@ -3187,12 +3196,14 @@ it.live("keeps claiming new work after repeated idle periods", () => }, }), ); - const workerLayer = effectWorkerLayerWithOptions({ + const workerLayer = EffectWorker.layerWithOptions({ workerId: "idle-wave-worker", - }).pipe(Layer.provide(Layer.merge(Layer.succeed(EffectOutboxV2, outbox), executorLayer))); + }).pipe( + Layer.provide(Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), executorLayer)), + ); yield* Effect.gen(function* () { - yield* runEffectWorkerDaemonWithOptions({ concurrency: 2 }).pipe(Effect.forkScoped); + yield* EffectWorker.runDaemonWithOptions({ concurrency: 2 }).pipe(Effect.forkScoped); for (let wave = 1; wave <= 6; wave += 1) { yield* Effect.sleep("125 millis"); const effectId = `effect:foundation-idle-wave:${wave}`; diff --git a/apps/server/src/orchestration-v2/GrokOrchestratorV2.live.test.ts b/apps/server/src/orchestration-v2/GrokOrchestratorV2.live.test.ts index ae9b4fd05db2..d1c7e20bd49c 100644 --- a/apps/server/src/orchestration-v2/GrokOrchestratorV2.live.test.ts +++ b/apps/server/src/orchestration-v2/GrokOrchestratorV2.live.test.ts @@ -1,5 +1,5 @@ import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; -import { SourceControlProviderRegistry } from "../sourceControl/SourceControlProviderRegistry.ts"; +import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it } from "@effect/vitest"; import { @@ -20,31 +20,30 @@ import { describe } from "vite-plus/test"; import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; import * as HostPowerMonitor from "../background/HostPowerMonitor.ts"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; -import { AntigravityInstallation } from "../provider/AntigravityInstallation.ts"; -import { CodexInstallation } from "../provider/CodexInstallation.ts"; -import { ServerEnvironmentIdentity } from "../environment/ServerEnvironment.ts"; +import * as AntigravityInstallation from "../provider/AntigravityInstallation.ts"; +import * as CodexInstallation from "../provider/CodexInstallation.ts"; +import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; import * as ModelManifest from "../provider/ModelManifest.ts"; import { ProviderInstanceRegistryHydrationLive } from "../provider/Layers/ProviderInstanceRegistryHydration.ts"; -import { - NoOpProviderEventLoggers, - ProviderEventLoggers, -} from "../provider/Layers/ProviderEventLoggers.ts"; -import { OpenCodeRuntimeLive } from "../provider/opencodeRuntime.ts"; +import * as ProviderEventLoggers from "../provider/Layers/ProviderEventLoggers.ts"; +import * as OpenCodeRuntime from "../provider/opencodeRuntime.ts"; import * as OpenCodeServerLedger from "../provider/OpenCodeServerLedger.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; +import * as ServerSettings from "../serverSettings.ts"; import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; import * as VcsProcess from "../vcs/VcsProcess.ts"; -import { OrchestratorV2 } from "./Orchestrator.ts"; +import * as Orchestrator from "./Orchestrator.ts"; import { worktreeRepairDependenciesTestLayer } from "./ProviderTurnStartService.testkit.ts"; import { OrchestrationV2LayerLive } from "./runtimeLayer.ts"; -import { layer as mcpSessionRegistryTestLayer } from "../mcp/McpSessionRegistry.testkit.ts"; +import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; import { GROK_MODEL_SELECTION } from "./testkit/fixtures/shared.ts"; const PlatformTestLayer = Layer.merge( NodeServices.layer, - Layer.mock(SourceControlProviderRegistry)({ resolveLink: () => Effect.die("unused title link") }), + Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ + resolveLink: () => Effect.die("unused title link"), + }), ); const serverConfigLayer = ServerConfig.layerTest(process.cwd(), { @@ -59,7 +58,7 @@ const vcsDriverRegistryLayer = VcsDriverRegistry.layer.pipe( const checkpointStoreLayer = CheckpointStore.layer.pipe(Layer.provide(vcsDriverRegistryLayer)); -const serverSettingsLayer = ServerSettingsService.layerTest({ +const serverSettingsLayer = ServerSettings.layerTest({ providers: { grok: { enabled: true }, }, @@ -79,20 +78,25 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe ), NodeServices.layer, FetchHttpClient.layer, - OpenCodeRuntimeLive.pipe( + OpenCodeRuntime.OpenCodeRuntimeLive.pipe( Layer.provide(OpenCodeServerLedger.layerTest), Layer.provide(PlatformTestLayer), ), - Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers), + Layer.succeed( + ProviderEventLoggers.ProviderEventLoggers, + ProviderEventLoggers.NoOpProviderEventLoggers, + ), ModelManifest.layerTest, - AntigravityInstallation.layer.pipe( + AntigravityInstallation.AntigravityInstallation.layer.pipe( Layer.provide(serverConfigLayer.pipe(Layer.provide(PlatformTestLayer))), Layer.provide(FetchHttpClient.layer), Layer.provide(PlatformTestLayer), ), // The Codex driver now resolves managed ChatGPT installs; these runs never launch Codex. - Layer.mock(CodexInstallation)({ managedDirectory: "unused-managed-installation" }), - Layer.succeed(ServerEnvironmentIdentity, { + Layer.mock(CodexInstallation.CodexInstallation)({ + managedDirectory: "unused-managed-installation", + }), + Layer.succeed(ServerEnvironment.ServerEnvironmentIdentity, { getEnvironmentId: Effect.succeed( EnvironmentId.make("00000000-0000-4000-8000-000000000001"), ), @@ -103,7 +107,7 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe const liveLayer = OrchestrationV2LayerLive.pipe( Layer.provide(worktreeRepairDependenciesTestLayer), - Layer.provide(mcpSessionRegistryTestLayer), + Layer.provide(McpSessionRegistryTestkit.layer), Layer.provide(SqlitePersistenceMemory), Layer.provide(checkpointStoreLayer), Layer.provide(serverConfigLayer), @@ -115,7 +119,7 @@ const liveLayer = OrchestrationV2LayerLive.pipe( ); const waitForIdle = Effect.fn("GrokOrchestratorV2Live.waitForIdle")(function* (threadId: ThreadId) { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; for (let attempt = 0; attempt < 600; attempt += 1) { const projection = yield* orchestrator.getThreadProjection(threadId); if ( @@ -136,7 +140,7 @@ describe.runIf(process.env.T3_GROK_LIVE_ORCHESTRATOR === "1")("Grok V2 live orch "forks through portable context using real Grok ACP agents", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const projectId = ProjectId.make("project:grok-live-portable-fork"); const sourceThreadId = ThreadId.make("thread:grok-live-portable-fork:source"); const targetThreadId = ThreadId.make("thread:grok-live-portable-fork:target"); diff --git a/apps/server/src/orchestration-v2/Orchestrator.control-reads.test.ts b/apps/server/src/orchestration-v2/Orchestrator.control-reads.test.ts index a296b0866cbd..fffacab5a065 100644 --- a/apps/server/src/orchestration-v2/Orchestrator.control-reads.test.ts +++ b/apps/server/src/orchestration-v2/Orchestrator.control-reads.test.ts @@ -19,8 +19,8 @@ import * as Layer from "effect/Layer"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; -import { OrchestratorV2 } from "./Orchestrator.ts"; -import { ProjectionStoreV2, layer as projectionLayer } from "./ProjectionStore.ts"; +import * as Orchestrator from "./Orchestrator.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; @@ -37,7 +37,7 @@ const adapter = { const database = SqlitePersistenceMemory; const testLayer = Layer.mergeAll( database, - projectionLayer.pipe(Layer.provide(database)), + ProjectionStore.layer.pipe(Layer.provide(database)), makeOrchestratorV2ReplayLayerWithRegistry( { name: "control-reads" }, ProviderAdapterRegistry.makeLayer([adapter]), @@ -49,8 +49,8 @@ it.effect( "dispatches metadata, queue resume and request controls without hydrating unrelated history", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const projections = yield* ProjectionStoreV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const sql = yield* SqlClient.SqlClient; const threadId = ThreadId.make("thread:control-dispatch"); const now = yield* DateTime.now; @@ -278,8 +278,8 @@ it.effect( it.effect("implements a proposed plan that the command projection leaves out", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const projections = yield* ProjectionStoreV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const threadId = ThreadId.make("thread:implement-plan"); const planId = PlanId.make("plan:implement-plan"); const now = yield* DateTime.now; diff --git a/apps/server/src/orchestration-v2/ProjectSettingsUpgrade.integration.test.ts b/apps/server/src/orchestration-v2/ProjectSettingsUpgrade.integration.test.ts index 127d8867aa8f..b056b27b1282 100644 --- a/apps/server/src/orchestration-v2/ProjectSettingsUpgrade.integration.test.ts +++ b/apps/server/src/orchestration-v2/ProjectSettingsUpgrade.integration.test.ts @@ -10,22 +10,22 @@ import * as Stream from "effect/Stream"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import * as GitWorkflow from "../git/GitWorkflowService.ts"; import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient"; -import { layer as mcpSessionRegistryTestLayer } from "../mcp/McpSessionRegistry.testkit.ts"; +import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; import { makeSqlitePersistenceLive } from "../persistence/Layers/Sqlite.ts"; import { runMigrations } from "../persistence/Migrations.ts"; -import { ProjectEnrichmentService } from "../project/ProjectEnrichmentService.ts"; +import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; import * as ProjectService from "../project/ProjectService.ts"; -import { ProviderInstanceRegistry } from "../provider/Services/ProviderInstanceRegistry.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; -import { SourceControlProviderRegistry } from "../sourceControl/SourceControlProviderRegistry.ts"; +import * as ProviderInstanceRegistry from "../provider/Services/ProviderInstanceRegistry.ts"; +import * as ServerSettings from "../serverSettings.ts"; +import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; import * as VcsProcess from "../vcs/VcsProcess.ts"; -import { WorkspacePaths } from "../workspace/WorkspacePaths.ts"; -import { LegacyV1ThreadImporter } from "./legacy/LegacyV1ThreadImporter.ts"; +import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; +import * as LegacyV1ThreadImporter from "./legacy/LegacyV1ThreadImporter.ts"; import { OrchestrationV2LayerLive, ProjectServiceLayerLive } from "./runtimeLayer.ts"; const projectId = ProjectId.make("project:upgrade"); @@ -119,9 +119,13 @@ const unusedEnrichment = { const makeRuntimeLayer = (dbPath: string) => { const platform = Layer.merge( NodeServices.layer, - Layer.mock(SourceControlProviderRegistry)({ resolveLink: () => Effect.die("unused") }), + Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ + resolveLink: () => Effect.die("unused"), + }), ); - const serverConfig = ServerConfig.layerTest(process.cwd(), { prefix: "t3-project-upgrade-" }); + const serverConfig = ServerConfig.layerTest(process.cwd(), { + prefix: "t3-project-upgrade-", + }); const checkpointStore = CheckpointStore.layer.pipe( Layer.provide( VcsDriverRegistry.layer.pipe( @@ -136,24 +140,24 @@ const makeRuntimeLayer = (dbPath: string) => { ProjectServiceLayerLive, ).pipe( Layer.provide( - Layer.mock(ProjectEnrichmentService)({ + Layer.mock(ProjectEnrichmentService.ProjectEnrichmentService)({ peek: () => Effect.succeed(unusedEnrichment), getAvailable: () => Effect.succeed(unusedEnrichment), invalidate: () => Effect.void, }), ), Layer.provide( - Layer.mock(WorkspacePaths)({ + Layer.mock(WorkspacePaths.WorkspacePaths)({ normalizeWorkspaceRoot: (workspaceRoot) => Effect.succeed(workspaceRoot), }), ), - Layer.provide(mcpSessionRegistryTestLayer), + Layer.provide(McpSessionRegistryTestkit.layer), Layer.provideMerge(makeSqlitePersistenceLive(dbPath)), Layer.provide(checkpointStore), Layer.provide(serverConfig), - Layer.provide(ServerSettingsService.layerTest()), + Layer.provide(ServerSettings.layerTest()), Layer.provide( - Layer.succeed(ProviderInstanceRegistry, { + Layer.succeed(ProviderInstanceRegistry.ProviderInstanceRegistry, { getInstance: () => Effect.succeed(undefined), listInstances: Effect.succeed([]), listUnavailable: Effect.succeed([]), @@ -181,7 +185,7 @@ it.live("keeps project settings through the V2 migrations and the first V2 boot" // Seed the released V1 schema, then boot the V2 runtime, which runs 055+, on the same file. yield* seedV1Database.pipe(Effect.provide(NodeSqliteClient.layer({ filename: dbPath }))); yield* Effect.gen(function* () { - yield* (yield* LegacyV1ThreadImporter).reconcileShells; + yield* (yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter).reconcileShells; assert.deepEqual(yield* readSettings, expectedSettings); const project = yield* (yield* ProjectService.ProjectService).getById(projectId); assert.equal(project._tag, "Some"); diff --git a/apps/server/src/orchestration-v2/ProjectionControlReads.test.ts b/apps/server/src/orchestration-v2/ProjectionControlReads.test.ts index 413b09d42b0e..f57c4ab7bc81 100644 --- a/apps/server/src/orchestration-v2/ProjectionControlReads.test.ts +++ b/apps/server/src/orchestration-v2/ProjectionControlReads.test.ts @@ -21,18 +21,10 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; -import { - ProjectionStoreV2, - ProjectionStoreThreadNotFoundError, - layer, - layerMemory, -} from "./ProjectionStore.ts"; -import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts"; -import { - layer as controlLayer, - ProviderTurnControlServiceV2, -} from "./ProviderTurnControlService.ts"; -import { layer as replyLayer, RuntimeRequestServiceV2 } from "./RuntimeRequestService.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; +import * as ProviderSessionManager from "./ProviderSessionManager.ts"; +import * as ProviderTurnControlService from "./ProviderTurnControlService.ts"; +import * as RuntimeRequestService from "./RuntimeRequestService.ts"; const threadId = ThreadId.make("thread:control-reads"); const providerThreadId = ProviderThreadId.make("provider-thread:control-reads"); @@ -196,10 +188,12 @@ function fixtureEvents(now: DateTime.Utc): ReadonlyArray Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const events = fixtureEvents(now).map((event) => event.type === "run-attempt.created" @@ -219,7 +213,7 @@ for (const storage of ["sqlite", "memory"] as const) { ); it.effect(`${storage}: controls and replies read only their exact durable targets`, () => Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const events = fixtureEvents(now); yield* Effect.forEach(events, (event) => store.apply(event), { discard: true }); @@ -270,11 +264,11 @@ for (const storage of ["sqlite", "memory"] as const) { const missingThread = yield* store .getRuntimeRequest(ThreadId.make("thread:missing"), requestId) .pipe(Effect.flip); - assert.instanceOf(missingThread, ProjectionStoreThreadNotFoundError); + assert.instanceOf(missingThread, ProjectionStore.ProjectionStoreThreadNotFoundError); const calls: string[] = []; let hasBackgroundWork = false; - const sessions = Layer.mock(ProviderSessionManagerV2)({ + const sessions = Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ get: () => Effect.succeed( Option.some({ @@ -300,8 +294,8 @@ for (const storage of ["sqlite", "memory"] as const) { ), }); yield* Effect.gen(function* () { - const control = yield* ProviderTurnControlServiceV2; - const reply = yield* RuntimeRequestServiceV2; + const control = yield* ProviderTurnControlService.ProviderTurnControlServiceV2; + const reply = yield* RuntimeRequestService.RuntimeRequestServiceV2; yield* control.interrupt({ threadId, providerThreadId, providerTurnId, providerSessionId }); yield* control.steer({ threadId, @@ -349,7 +343,13 @@ for (const storage of ["sqlite", "memory"] as const) { hasBackgroundWork = true; yield* control.interrupt({ threadId, providerThreadId, providerTurnId, providerSessionId }); assert.deepEqual(calls, ["interrupt", "Use the smaller fix.", "reply", "interrupt"]); - }).pipe(Effect.provide(Layer.merge(controlLayer, replyLayer).pipe(Layer.provide(sessions)))); + }).pipe( + Effect.provide( + Layer.merge(ProviderTurnControlService.layer, RuntimeRequestService.layer).pipe( + Layer.provide(sessions), + ), + ), + ); }).pipe(Effect.provide(Layer.merge(storeLayer, SqlitePersistenceMemory))), ); } diff --git a/apps/server/src/orchestration-v2/ProjectionMaintenance.ts b/apps/server/src/orchestration-v2/ProjectionMaintenance.ts index e805a9fbb057..e2386bb26930 100644 --- a/apps/server/src/orchestration-v2/ProjectionMaintenance.ts +++ b/apps/server/src/orchestration-v2/ProjectionMaintenance.ts @@ -7,11 +7,8 @@ import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; import * as SqlClient from "effect/unstable/sql/SqlClient"; -import { EventStoreV2 } from "./EventStore.ts"; -import { - ORCHESTRATION_V2_PROJECTION_SCHEMA_VERSION, - ProjectionStoreV2, -} from "./ProjectionStore.ts"; +import * as EventStore from "./EventStore.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; export interface ProjectionVerificationV2 { readonly valid: boolean; @@ -61,13 +58,13 @@ const encodeEntityKey = Schema.encodeSync( export const layer: Layer.Layer< ProjectionMaintenanceV2, never, - EventStoreV2 | ProjectionStoreV2 | SqlClient.SqlClient + EventStore.EventStoreV2 | ProjectionStore.ProjectionStoreV2 | SqlClient.SqlClient > = Layer.effect( ProjectionMaintenanceV2, Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - const eventStore = yield* EventStoreV2; - const projectionStore = yield* ProjectionStoreV2; + const eventStore = yield* EventStore.EventStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; /** * EventSink commits the event, its projection updates, and projection metadata in one SQL @@ -108,7 +105,7 @@ export const layer: Layer.Layer< const projectionSequence = metadata[0]?.last_sequence ?? 0; return { valid: - schemaVersion === ORCHESTRATION_V2_PROJECTION_SCHEMA_VERSION && + schemaVersion === ProjectionStore.ORCHESTRATION_V2_PROJECTION_SCHEMA_VERSION && projectionSequence === expectedSequence && missingThreadIds.length === 0 && unexpectedThreadIds.length === 0 && @@ -184,7 +181,7 @@ export const layer: Layer.Layer< ) VALUES ( 'thread-projections', - ${ORCHESTRATION_V2_PROJECTION_SCHEMA_VERSION}, + ${ProjectionStore.ORCHESTRATION_V2_PROJECTION_SCHEMA_VERSION}, ${lastSequence}, ${now} ) diff --git a/apps/server/src/orchestration-v2/ProjectionRecovery.test.ts b/apps/server/src/orchestration-v2/ProjectionRecovery.test.ts index f8006e97a0f4..8db18a0d5f08 100644 --- a/apps/server/src/orchestration-v2/ProjectionRecovery.test.ts +++ b/apps/server/src/orchestration-v2/ProjectionRecovery.test.ts @@ -24,11 +24,11 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; -import { EffectOutboxV2, layer as effectOutboxLayer } from "./EffectOutbox.ts"; -import { ProjectionStoreV2, layer as projectionStoreLayer } from "./ProjectionStore.ts"; +import * as EffectOutbox from "./EffectOutbox.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; import { restartContinuationRun } from "./RestartContinuation.ts"; -const TestLayer = Layer.mergeAll(projectionStoreLayer, effectOutboxLayer).pipe( +const TestLayer = Layer.mergeAll(ProjectionStore.layer, EffectOutbox.layer).pipe( Layer.provideMerge(SqlitePersistenceMemory), ); const providerInstanceId = ProviderInstanceId.make("codex"); @@ -39,7 +39,7 @@ const createThread = Effect.fn(function* ( name: string, overrides: Partial = {}, ) { - const projections = yield* ProjectionStoreV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const threadId = ThreadId.make(`thread:recovery:${name}`); const thread: OrchestrationV2AppThread = { @@ -81,7 +81,7 @@ const createRun = Effect.fn(function* ( status: OrchestrationV2Run["status"], overrides: Partial = {}, ) { - const projections = yield* ProjectionStoreV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const ordinal = overrides.ordinal ?? 1; const runId = RunId.make(`run:${threadId}:${ordinal}`); @@ -115,8 +115,8 @@ const createRun = Effect.fn(function* ( it.effect("selects unfinished recovery work without reading settled thread histories", () => Effect.gen(function* () { - const projections = yield* ProjectionStoreV2; - const outbox = yield* EffectOutboxV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const sql = yield* SqlClient.SqlClient; const now = yield* DateTime.now; for (let index = 0; index < 600; index += 1) { @@ -226,7 +226,7 @@ it.effect("selects unfinished recovery work without reading settled thread histo it.effect("recovers terminal subagent results until their cross-thread transfer exists", () => Effect.gen(function* () { - const projections = yield* ProjectionStoreV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const sql = yield* SqlClient.SqlClient; const now = yield* DateTime.now; const parent = yield* createThread("subagent-parent"); @@ -288,7 +288,7 @@ it.effect("recovers terminal subagent results until their cross-thread transfer it.effect("includes shared sessions and provider-owned background rosters in recovery", () => Effect.gen(function* () { - const projections = yield* ProjectionStoreV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const first = yield* createThread("shared-first"); const second = yield* createThread("shared-second", { archivedAt: now }); @@ -422,7 +422,7 @@ it.effect("includes shared sessions and provider-owned background rosters in rec it.effect("marks fork descendants unreadable when their source is missing or corrupt", () => Effect.gen(function* () { - const projections = yield* ProjectionStoreV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const sql = yield* SqlClient.SqlClient; const source = yield* createThread("source"); const sourceRun = yield* createRun(source, "completed"); diff --git a/apps/server/src/orchestration-v2/ProjectionSettlement.test.ts b/apps/server/src/orchestration-v2/ProjectionSettlement.test.ts index c1bb758649d3..4fb806e61e9c 100644 --- a/apps/server/src/orchestration-v2/ProjectionSettlement.test.ts +++ b/apps/server/src/orchestration-v2/ProjectionSettlement.test.ts @@ -22,14 +22,10 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as Statement from "effect/unstable/sql/Statement"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; -import { - ProjectionStoreV2, - layer as projectionStoreLayer, - layerMemory, -} from "./ProjectionStore.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; import { isAutoSettlementCandidate, resolveAutoSettlementAt } from "./ThreadSettlementService.ts"; -const SqlLayer = projectionStoreLayer.pipe(Layer.provideMerge(SqlitePersistenceMemory)); +const SqlLayer = ProjectionStore.layer.pipe(Layer.provideMerge(SqlitePersistenceMemory)); const now = DateTime.makeUnsafe("2026-09-04T12:00:00Z"); const old = DateTime.subtract(now, { days: 10 }); const providerInstanceId = ProviderInstanceId.make("codex"); @@ -40,7 +36,7 @@ const createThread = Effect.fn(function* ( name: string, overrides: Partial = {}, ) { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const threadId = ThreadId.make(`thread:settlement:${name}`); const thread: OrchestrationV2AppThread = { createdBy: "user", @@ -81,7 +77,7 @@ const createRun = Effect.fn(function* ( status: OrchestrationV2Run["status"] = "completed", ordinal = 1, ) { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const runId = RunId.make(`run:${threadId}:${ordinal}`); yield* store.apply({ id: EventId.make(`event:${runId}`), @@ -115,7 +111,7 @@ const createItem = Effect.fn(function* ( status: "idle" | "running" | "completed", persistent = false, ) { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; yield* store.apply({ id: EventId.make(`event:item:${runId}`), type: "turn-item.updated", @@ -146,13 +142,13 @@ const createItem = Effect.fn(function* ( for (const [name, testLayer] of [ ["sql", SqlLayer], - ["memory", layerMemory], + ["memory", ProjectionStore.layerMemory], ] as const) { it.effect( `${name}: discovers settlement work with the same activity and background semantics as the shell`, () => Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const idle = yield* createThread("idle"); const completed = yield* createThread("completed"); yield* createRun(completed); @@ -311,11 +307,11 @@ const pullRequestLink = (number: number) => ({ for (const [name, testLayer] of [ ["sql", SqlLayer], - ["memory", layerMemory], + ["memory", ProjectionStore.layerMemory], ] as const) { it.effect(`${name}: lists only active threads with pull request links, oldest first`, () => Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; yield* createThread("no-links"); yield* createThread("empty-links", { pullRequests: [] }); yield* createThread("archived-link", { @@ -346,11 +342,11 @@ for (const [name, testLayer] of [ for (const [name, testLayer] of [ ["sql", SqlLayer], - ["memory", layerMemory], + ["memory", ProjectionStore.layerMemory], ] as const) { it.effect(`${name}: an unsettled-only shell read skips settled threads`, () => Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const open = yield* createThread("unsettled-open"); const reopened = yield* createThread("unsettled-reopened", { settledOverride: "active" }); yield* createThread("unsettled-manual", { settledOverride: "settled", settledAt: old }); @@ -373,7 +369,7 @@ it.effect( "reads settlement candidates and thread metadata without loading historical or archived payloads", () => Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const sql = yield* SqlClient.SqlClient; const source = yield* createThread("historical-source", { archivedAt: old }); const sourceRun = yield* createRun(source); @@ -436,7 +432,7 @@ it.effect( it.effect("shell failure lookups stay on the thread's own turn items", () => Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const sql = yield* SqlClient.SqlClient; const failed = yield* createThread("failed-latest"); yield* createRun(failed, "failed"); diff --git a/apps/server/src/orchestration-v2/ProjectionStore.test.ts b/apps/server/src/orchestration-v2/ProjectionStore.test.ts index 3c67ffa892a8..9efefed80483 100644 --- a/apps/server/src/orchestration-v2/ProjectionStore.test.ts +++ b/apps/server/src/orchestration-v2/ProjectionStore.test.ts @@ -30,14 +30,7 @@ import { projectThreadAwarenessV2 } from "@t3tools/shared/agentAwareness"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; -import { - isTurnItemAtOrBeforeRun, - layerMemory as projectionStoreMemoryLayer, - threadShellFromProjection, - ProjectionStoreV2, - ProjectionStoreThreadNotFoundError, - layer as projectionStoreLayer, -} from "./ProjectionStore.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; import { buildBoundedThreadProjection, decodeThreadHistoryCursor, @@ -46,7 +39,7 @@ import { } from "./threadHistoryPaging.ts"; const TestLayer = Layer.mergeAll( - projectionStoreLayer.pipe(Layer.provideMerge(SqlitePersistenceMemory)), + ProjectionStore.layer.pipe(Layer.provideMerge(SqlitePersistenceMemory)), SqlitePersistenceMemory, ); const modelSelection = { @@ -60,7 +53,7 @@ const encodeUnknownJsonString = Schema.encodeSync(Schema.fromJsonString(Schema.U const addRolledBackRecoveryCandidate = Effect.fn("addRolledBackRecoveryCandidate")(function* ( suffix: string, ) { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const threadId = ThreadId.make(`thread:${suffix}:rolled-back`); const runId = RunId.make(`run:${suffix}:rolled-back`); @@ -171,7 +164,7 @@ const addRolledBackRecoveryCandidate = Effect.fn("addRolledBackRecoveryCandidate const addOrphanedRecoveryCandidate = Effect.fn("addOrphanedRecoveryCandidate")(function* ( suffix: string, ) { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const threadId = ThreadId.make(`thread:${suffix}:orphaned`); const runId = RunId.make(`run:${suffix}:missing`); @@ -250,7 +243,7 @@ it("includes imported runless history when selecting fork context through a run" ]); assert.isTrue( - isTurnItemAtOrBeforeRun({ + ProjectionStore.isTurnItemAtOrBeforeRun({ historyOrigin: "v1_import", itemRunId: null, runOrdinalById, @@ -258,7 +251,7 @@ it("includes imported runless history when selecting fork context through a run" }), ); assert.isFalse( - isTurnItemAtOrBeforeRun({ + ProjectionStore.isTurnItemAtOrBeforeRun({ historyOrigin: undefined, itemRunId: null, runOrdinalById, @@ -266,7 +259,7 @@ it("includes imported runless history when selecting fork context through a run" }), ); assert.isTrue( - isTurnItemAtOrBeforeRun({ + ProjectionStore.isTurnItemAtOrBeforeRun({ historyOrigin: "v1_import", itemRunId: firstRunId, runOrdinalById, @@ -274,7 +267,7 @@ it("includes imported runless history when selecting fork context through a run" }), ); assert.isFalse( - isTurnItemAtOrBeforeRun({ + ProjectionStore.isTurnItemAtOrBeforeRun({ historyOrigin: "v1_import", itemRunId: secondRunId, runOrdinalById, @@ -285,7 +278,7 @@ it("includes imported runless history when selecting fork context through a run" // Recovery records the work, then a run.updated snapshot taken before it lands. const restartCancelledWorkSurvivesStaleRunUpdate = Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const threadId = yield* addRolledBackRecoveryCandidate("restart-cancelled-work-stale-update"); const run = (yield* store.getThreadProjection(threadId)).runs[0]!; const now = yield* DateTime.now; @@ -314,25 +307,25 @@ const restartCancelledWorkSurvivesStaleRunUpdate = Effect.gen(function* () { }); it.effect("memory projection keeps restart-cancelled work through a stale run.updated", () => - restartCancelledWorkSurvivesStaleRunUpdate.pipe(Effect.provide(projectionStoreMemoryLayer)), + restartCancelledWorkSurvivesStaleRunUpdate.pipe(Effect.provide(ProjectionStore.layerMemory)), ); it.effect("memory recovery selection ignores unfinished items from rolled-back runs", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const threadId = yield* addRolledBackRecoveryCandidate("memory-recovery-candidates"); assert.notInclude(yield* projectionStore.getRecoveryThreadIds("runtime"), threadId); - }).pipe(Effect.provide(projectionStoreMemoryLayer)), + }).pipe(Effect.provide(ProjectionStore.layerMemory)), ); it.effect("memory recovery selection includes unfinished items from missing runs", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const threadId = yield* addOrphanedRecoveryCandidate("memory-recovery-candidates"); assert.include(yield* projectionStore.getRecoveryThreadIds("runtime"), threadId); - }).pipe(Effect.provide(projectionStoreMemoryLayer)), + }).pipe(Effect.provide(ProjectionStore.layerMemory)), ); it.layer(TestLayer)("ProjectionStoreV2", (it) => { @@ -342,7 +335,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { ); it.effect("records restart-cancelled work without regressing a run that completed since", () => Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const threadId = yield* addRolledBackRecoveryCandidate("restart-cancelled-work"); const run = (yield* store.getThreadProjection(threadId)).runs[0]!; const now = yield* DateTime.now; @@ -378,7 +371,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { ); it.effect("limits turn-start history to the requested runs, including an empty selection", () => Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const threadId = yield* addRolledBackRecoveryCandidate("selected-turn-start-history"); const runId = (yield* store.getThreadProjection(threadId)).runs[0]!.id; const history = yield* store.getTurnStartHistory(threadId); @@ -390,7 +383,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { ); it.effect("preserves stored provider usage when a terminal update omits it", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:provider-usage-reload"); const providerThreadId = ProviderThreadId.make("provider-thread:provider-usage-reload"); @@ -497,7 +490,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { it.effect("pages complete user turns through SQL regardless of tool count or payload size", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const sql = yield* SqlClient.SqlClient; const now = yield* DateTime.now; const nowIso = DateTime.formatIso(now); @@ -648,7 +641,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { "bounds completed nodes within one long run while preserving ancestry and live work", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:bounded-node-history"); yield* projectionStore.apply({ @@ -827,7 +820,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { it.effect("reads a fixed SQL turn-item window for long histories and repeated clients", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const sql = yield* SqlClient.SqlClient; const now = yield* DateTime.now; const nowIso = DateTime.formatIso(now); @@ -1389,7 +1382,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { it.effect("scopes actionable provider state to the requested bounded thread", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const projectId = ProjectId.make("project:bounded-provider-scope"); const targetThreadId = ThreadId.make("thread:bounded-provider-scope:target"); @@ -1489,7 +1482,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { it.effect("projects root provider owners into the shell in first-use order", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const projectId = ProjectId.make("project:provider-history"); const threadId = ThreadId.make("thread:provider-history"); @@ -1575,7 +1568,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { it.effect("does not treat visited or marked-unread state as thread activity", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const createdAt = yield* DateTime.now; const visitedOccurredAt = DateTime.add(createdAt, { seconds: 1 }); const markedUnreadOccurredAt = DateTime.add(createdAt, { seconds: 2 }); @@ -1644,7 +1637,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { it.effect("preserves delegated completion ownership across stale run and task updates", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const later = DateTime.add(now, { seconds: 1 }); const threadId = ThreadId.make("thread:projection-delegated-completion"); @@ -1794,7 +1787,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { it.effect("only exposes interruptible runs through the shell activeRunId", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:projection-shell-interruptible"); const projectId = ProjectId.make("project:projection-shell-interruptible"); @@ -1927,7 +1920,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { const sqlShell = (yield* projectionStore.getShellSnapshot()).threads.find( (row) => row.id === threadId, )!; - const memoryShell = threadShellFromProjection(projection); + const memoryShell = ProjectionStore.threadShellFromProjection(projection); const expected = activityStatus === "completed" ? null : DateTime.toEpochMillis(now); const timestamp = (value: DateTime.Utc | null | undefined) => value == null ? null : DateTime.toEpochMillis(value); @@ -1941,7 +1934,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { it.effect("selects only threads with runtime state that needs recovery", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const settledThreadId = ThreadId.make("thread:recovery-candidates:settled"); const runningThreadId = ThreadId.make("thread:recovery-candidates:running"); @@ -2026,7 +2019,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { it.effect("projects only the latest failed root turn's limit into SQL and memory shells", () => Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const threadId = yield* addRolledBackRecoveryCandidate("limit-shell"); const otherThreadId = yield* addRolledBackRecoveryCandidate("other-limit-shell"); const original = (yield* store.getThreadProjection(threadId)).runs[0]!; @@ -2068,7 +2061,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { lastErrorClass: string | null, ) { const projection = yield* store.getThreadProjection(threadId); - const memoryShell = threadShellFromProjection(projection); + const memoryShell = ProjectionStore.threadShellFromProjection(projection); const shells = yield* store.getShellSnapshot(); const sqlShell = shells.threads.find((row) => row.id === threadId)!; for (const shell of [memoryShell, sqlShell]) { @@ -2136,7 +2129,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { }); yield* assertSummary("Plan limit reached.", "usage_limit"); const queuedProjection = yield* store.getThreadProjection(threadId); - const queuedMemoryShell = threadShellFromProjection(queuedProjection); + const queuedMemoryShell = ProjectionStore.threadShellFromProjection(queuedProjection); const queuedSqlShell = (yield* store.getShellSnapshot()).threads.find( (row) => row.id === threadId, )!; @@ -2371,7 +2364,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { it.effect("projects one shared provider session into multiple thread bindings", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const projectId = ProjectId.make("project:projection-shared-provider-session"); const firstThreadId = ThreadId.make("thread:projection-shared-provider-session:first"); @@ -2498,7 +2491,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { "reads checkpoint context without decoding transcript or checkpoint file payloads", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const sql = yield* SqlClient.SqlClient; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:checkpoint-context"); @@ -2626,13 +2619,13 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { const missing = yield* projectionStore .getCheckpointContext(ThreadId.make("thread:checkpoint-context:missing")) .pipe(Effect.flip); - assert.instanceOf(missing, ProjectionStoreThreadNotFoundError); + assert.instanceOf(missing, ProjectionStore.ProjectionStoreThreadNotFoundError); }), ); it.effect("builds shell snapshots without decoding full turn item payloads", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const sql = yield* SqlClient.SqlClient; const now = yield* DateTime.now; const nowIso = DateTime.formatIso(now); @@ -2750,7 +2743,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { it.effect("counts imported runless history inherited by fork shells", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const projectId = ProjectId.make("project:projection-imported-fork-shell"); const sourceThreadId = ThreadId.make("thread:projection-imported-fork-shell:source"); @@ -2902,7 +2895,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { it.effect("removes rolled back runs from the active visible projection", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:projection-rollback-prune"); const projectId = ProjectId.make("project:projection-rollback-prune"); @@ -3315,7 +3308,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { it.effect("keeps fork visible items stable after a source run is rolled back", () => Effect.gen(function* () { - const projectionStore = yield* ProjectionStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const sql = yield* SqlClient.SqlClient; const now = yield* DateTime.now; const projectId = ProjectId.make("project:projection-fork-source-rollback"); @@ -4184,7 +4177,7 @@ it.layer(TestLayer)("ProjectionStoreV2", (it) => { it.effect("dates a completion released by held background work at the release", () => Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const threadId = ThreadId.make("thread:held-completion"); const runId = RunId.make("run:held-completion"); const providerThreadId = ProviderThreadId.make("provider-thread:held-completion"); diff --git a/apps/server/src/orchestration-v2/ProviderAdapterRegistry.test.ts b/apps/server/src/orchestration-v2/ProviderAdapterRegistry.test.ts index acc9ea0db5b7..ed9b8cd3df66 100644 --- a/apps/server/src/orchestration-v2/ProviderAdapterRegistry.test.ts +++ b/apps/server/src/orchestration-v2/ProviderAdapterRegistry.test.ts @@ -21,17 +21,13 @@ import * as Stream from "effect/Stream"; import * as ProviderAuthFlow from "../provider/ProviderAuthFlow.ts"; import type { ProviderAuthController } from "../provider/Services/ProviderAuthService.ts"; import type { ProviderInstance } from "../provider/ProviderDriver.ts"; -import { ProviderInstanceRegistry } from "../provider/Services/ProviderInstanceRegistry.ts"; +import * as ProviderInstanceRegistry from "../provider/Services/ProviderInstanceRegistry.ts"; import { ProviderAdapterOpenSessionError, type ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; import { ProviderAdapterDriverCreateError, type ProviderAdapterDriver, } from "./ProviderAdapterDriver.ts"; -import { - layerFromProviderInstanceRegistry, - makeRegistryFromConfigMap, - ProviderAdapterRegistryV2, -} from "./ProviderAdapterRegistry.ts"; +import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; const driver = ProviderDriverKind.make("codex"); const personalId = ProviderInstanceId.make("codex_personal"); @@ -69,7 +65,7 @@ const instances = [ makeInstance(personalId, personalAdapter), makeInstance(workId, workAdapter), ] as const; -const instanceRegistryLayer = Layer.succeed(ProviderInstanceRegistry, { +const instanceRegistryLayer = Layer.succeed(ProviderInstanceRegistry.ProviderInstanceRegistry, { getInstance: (instanceId) => Effect.succeed(instances.find((instance) => instance.instanceId === instanceId)), listInstances: Effect.succeed(instances), @@ -77,11 +73,13 @@ const instanceRegistryLayer = Layer.succeed(ProviderInstanceRegistry, { streamChanges: Stream.empty, subscribeChanges: Effect.never, }); -const TestLayer = layerFromProviderInstanceRegistry.pipe(Layer.provide(instanceRegistryLayer)); +const TestLayer = ProviderAdapterRegistry.layerFromProviderInstanceRegistry.pipe( + Layer.provide(instanceRegistryLayer), +); it.effect("routes two configured instances of the same driver independently", () => Effect.gen(function* () { - const registry = yield* ProviderAdapterRegistryV2; + const registry = yield* ProviderAdapterRegistry.ProviderAdapterRegistryV2; assert.strictEqual(yield* registry.get(personalId), personalAdapter); assert.strictEqual(yield* registry.get(workId), workAdapter); @@ -128,7 +126,7 @@ it.effect("closes a partially-created adapter scope immediately on typed failure yield* Effect.scoped( Effect.gen(function* () { - const exit = yield* makeRegistryFromConfigMap({ + const exit = yield* ProviderAdapterRegistry.makeRegistryFromConfigMap({ drivers: [makeLifecycleDriver(trackedCreate(releases, Effect.fail(createError)))], configMap: lifecycleConfigMap, }).pipe(Effect.exit); @@ -148,7 +146,7 @@ it.effect("closes a partially-created adapter scope immediately on defect", () = yield* Effect.scoped( Effect.gen(function* () { - const exit = yield* makeRegistryFromConfigMap({ + const exit = yield* ProviderAdapterRegistry.makeRegistryFromConfigMap({ drivers: [ makeLifecycleDriver(trackedCreate(releases, Effect.die("expected test defect"))), ], @@ -171,7 +169,7 @@ it.effect("closes a partially-created adapter scope immediately on interruption" yield* Effect.scoped( Effect.gen(function* () { - const fiber = yield* makeRegistryFromConfigMap({ + const fiber = yield* ProviderAdapterRegistry.makeRegistryFromConfigMap({ drivers: [ makeLifecycleDriver( trackedCreate( @@ -202,7 +200,7 @@ it.effect("keeps a successfully-created adapter scope open until normal release" yield* Effect.scoped( Effect.gen(function* () { - const registry = yield* makeRegistryFromConfigMap({ + const registry = yield* ProviderAdapterRegistry.makeRegistryFromConfigMap({ drivers: [makeLifecycleDriver(trackedCreate(releases, Effect.succeed(lifecycleAdapter)))], configMap: lifecycleConfigMap, }); @@ -234,11 +232,13 @@ it.effect( { ...instances[0], auth }, { ...instances[1], auth: { ...auth, isChangingCredentials: Effect.succeed(true) } }, ]; - const registry = yield* Effect.service(ProviderAdapterRegistryV2).pipe( + const registry = yield* Effect.service( + ProviderAdapterRegistry.ProviderAdapterRegistryV2, + ).pipe( Effect.provide( - layerFromProviderInstanceRegistry.pipe( + ProviderAdapterRegistry.layerFromProviderInstanceRegistry.pipe( Layer.provide( - Layer.mock(ProviderInstanceRegistry)({ + Layer.mock(ProviderInstanceRegistry.ProviderInstanceRegistry)({ getInstance: (id) => Effect.succeed(related.find((instance) => instance.instanceId === id)), listInstances: Effect.succeed(related), @@ -296,11 +296,11 @@ it.effect("interrupts admitted session startup when a shared peer signs out", () { ...instances[0], auth }, { ...instances[1], auth: peerAuth, orchestrationAdapter: adapter }, ]; - const registry = yield* Effect.service(ProviderAdapterRegistryV2).pipe( + const registry = yield* Effect.service(ProviderAdapterRegistry.ProviderAdapterRegistryV2).pipe( Effect.provide( - layerFromProviderInstanceRegistry.pipe( + ProviderAdapterRegistry.layerFromProviderInstanceRegistry.pipe( Layer.provide( - Layer.mock(ProviderInstanceRegistry)({ + Layer.mock(ProviderInstanceRegistry.ProviderInstanceRegistry)({ getInstance: (id) => Effect.succeed(related.find((instance) => instance.instanceId === id)), listInstances: Effect.succeed(related), diff --git a/apps/server/src/orchestration-v2/ProviderAdapterRegistry.ts b/apps/server/src/orchestration-v2/ProviderAdapterRegistry.ts index 456eb5f188ce..5279100e6206 100644 --- a/apps/server/src/orchestration-v2/ProviderAdapterRegistry.ts +++ b/apps/server/src/orchestration-v2/ProviderAdapterRegistry.ts @@ -13,18 +13,12 @@ import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; import * as Scope from "effect/Scope"; -import { ProviderInstanceRegistry } from "../provider/Services/ProviderInstanceRegistry.ts"; +import * as ProviderInstanceRegistry from "../provider/Services/ProviderInstanceRegistry.ts"; import { ProviderAdapterDriverCreateError, type AnyProviderAdapterDriver, } from "./ProviderAdapterDriver.ts"; -import { - ProviderAdapterV2, - ProviderAdapterOpenSessionError, - type ProviderAdapterV2Shape, - type ProviderAdapterV2SessionRuntime, - type ProviderAdapterV2Error, -} from "./ProviderAdapter.ts"; +import * as ProviderAdapter from "./ProviderAdapter.ts"; const isProviderSetupError = Schema.is(ProviderSetupError); @@ -54,11 +48,11 @@ export type ProviderAdapterRegistryV2Error = typeof ProviderAdapterRegistryV2Err export interface ProviderAdapterRegistryV2Shape { readonly get: ( instanceId: ProviderInstanceId, - ) => Effect.Effect; + ) => Effect.Effect; readonly list: () => Effect.Effect>; readonly getMetadata?: (instanceId: ProviderInstanceId) => Effect.Effect< { - readonly driver: ProviderAdapterV2Shape["driver"]; + readonly driver: ProviderAdapter.ProviderAdapterV2Shape["driver"]; readonly continuationKey: string; readonly enabled: boolean; readonly capabilities: OrchestrationV2ProviderCapabilities; @@ -80,11 +74,11 @@ export class ProviderAdapterRegistryV2 extends Context.Service< export const layerFromProviderInstanceRegistry: Layer.Layer< ProviderAdapterRegistryV2, never, - ProviderInstanceRegistry + ProviderInstanceRegistry.ProviderInstanceRegistry > = Layer.effect( ProviderAdapterRegistryV2, Effect.gen(function* () { - const instances = yield* ProviderInstanceRegistry; + const instances = yield* ProviderInstanceRegistry.ProviderInstanceRegistry; return ProviderAdapterRegistryV2.of({ get: (instanceId) => instances.getInstance(instanceId).pipe( @@ -118,8 +112,8 @@ export const layerFromProviderInstanceRegistry: Layer.Layer< }); } let admitted: Effect.Effect< - ProviderAdapterV2SessionRuntime, - ProviderAdapterV2Error | ProviderSetupError, + ProviderAdapter.ProviderAdapterV2SessionRuntime, + ProviderAdapter.ProviderAdapterV2Error | ProviderSetupError, Scope.Scope > = adapter.openSession(input); // Shared credential changes must interrupt a peer's startup too. @@ -131,7 +125,7 @@ export const layerFromProviderInstanceRegistry: Layer.Layer< return open.pipe( Effect.mapError((cause) => isProviderSetupError(cause) - ? new ProviderAdapterOpenSessionError({ + ? new ProviderAdapter.ProviderAdapterOpenSessionError({ driver: adapter.driver, providerSessionId: input.providerSessionId, cause, @@ -140,7 +134,7 @@ export const layerFromProviderInstanceRegistry: Layer.Layer< ), ); }, - } satisfies ProviderAdapterV2Shape); + } satisfies ProviderAdapter.ProviderAdapterV2Shape); }), ), list: () => @@ -175,7 +169,7 @@ export const ProviderAdapterRegistryBuildError = Schema.Union([ProviderAdapterDr export type ProviderAdapterRegistryBuildError = typeof ProviderAdapterRegistryBuildError.Type; function makeRegistry( - adapters: ReadonlyArray, + adapters: ReadonlyArray, ): ProviderAdapterRegistryV2Shape { return { get: (instanceId) => @@ -191,7 +185,7 @@ function makeRegistry( } export function makeLayer( - adapters: ReadonlyArray, + adapters: ReadonlyArray, ): Layer.Layer { return Layer.succeed( ProviderAdapterRegistryV2, @@ -200,7 +194,7 @@ export function makeLayer( } export function makeLayerEffect( - adapters: Effect.Effect, E, R>, + adapters: Effect.Effect, E, R>, ): Layer.Layer { return Layer.effect( ProviderAdapterRegistryV2, @@ -209,7 +203,7 @@ export function makeLayerEffect( } export function makeSingleLayer( - adapter: ProviderAdapterV2Shape, + adapter: ProviderAdapter.ProviderAdapterV2Shape, ): Layer.Layer { return makeLayer([adapter]); } @@ -223,7 +217,7 @@ const decodedConfigEnabled = (config: unknown): boolean | undefined => { }; interface LiveAdapterEntry { - readonly adapter: ProviderAdapterV2Shape; + readonly adapter: ProviderAdapter.ProviderAdapterV2Shape; readonly scope: Scope.Closeable; readonly entry: ProviderInstanceConfig; } @@ -356,17 +350,20 @@ export function makeDriverLayer(input: { ) as Layer.Layer; } -const layerFromProviderAdapter: Layer.Layer = - Layer.effect( - ProviderAdapterRegistryV2, - Effect.gen(function* () { - const adapter = yield* ProviderAdapterV2; - return ProviderAdapterRegistryV2.of({ - get: (instanceId) => - adapter.instanceId === instanceId - ? Effect.succeed(adapter) - : Effect.fail(new ProviderAdapterRegistryLookupError({ instanceId })), - list: () => Effect.succeed([adapter.instanceId]), - } satisfies ProviderAdapterRegistryV2Shape); - }), - ); +const layerFromProviderAdapter: Layer.Layer< + ProviderAdapterRegistryV2, + never, + ProviderAdapter.ProviderAdapterV2 +> = Layer.effect( + ProviderAdapterRegistryV2, + Effect.gen(function* () { + const adapter = yield* ProviderAdapter.ProviderAdapterV2; + return ProviderAdapterRegistryV2.of({ + get: (instanceId) => + adapter.instanceId === instanceId + ? Effect.succeed(adapter) + : Effect.fail(new ProviderAdapterRegistryLookupError({ instanceId })), + list: () => Effect.succeed([adapter.instanceId]), + } satisfies ProviderAdapterRegistryV2Shape); + }), +); diff --git a/apps/server/src/orchestration-v2/ProviderContinuationService.test.ts b/apps/server/src/orchestration-v2/ProviderContinuationService.test.ts index 69a6b584964f..3f62ab126a60 100644 --- a/apps/server/src/orchestration-v2/ProviderContinuationService.test.ts +++ b/apps/server/src/orchestration-v2/ProviderContinuationService.test.ts @@ -18,14 +18,10 @@ import * as Ref from "effect/Ref"; import * as Semaphore from "effect/Semaphore"; import * as TestClock from "effect/testing/TestClock"; -import { layer as idAllocatorLayer } from "./IdAllocator.ts"; -import { - type ProviderContinuationRequest, - ProviderContinuationRequests, - layer as continuationRequestsLayer, -} from "./ProviderContinuationRequests.ts"; -import { workerLive } from "./ProviderContinuationService.ts"; -import { ThreadManagementService } from "./ThreadManagementService.ts"; +import * as IdAllocator from "./IdAllocator.ts"; +import * as ProviderContinuationRequests from "./ProviderContinuationRequests.ts"; +import * as ProviderContinuationService from "./ProviderContinuationService.ts"; +import * as ThreadManagementService from "./ThreadManagementService.ts"; const threadId = ThreadId.make("thread-provider-continuation"); const providerThreadId = ProviderThreadId.make("provider-thread-continuation"); @@ -38,9 +34,9 @@ const projection = { } as unknown as OrchestrationV2ThreadProjection; const request = ( - dispatchIfCurrent?: ProviderContinuationRequest["dispatchIfCurrent"], + dispatchIfCurrent?: ProviderContinuationRequests.ProviderContinuationRequest["dispatchIfCurrent"], detail: string | null = null, -): ProviderContinuationRequest => ({ +): ProviderContinuationRequests.ProviderContinuationRequest => ({ threadId, providerThreadId, driver, @@ -96,14 +92,14 @@ function testLayer(input: { readonly dispatched: Queue.Queue; readonly getThreadRecords: () => Effect.Effect; }) { - const threads = Layer.mock(ThreadManagementService)({ + const threads = Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: input.getThreadRecords, dispatch: (command) => Queue.offer(input.dispatched, command).pipe(Effect.as({} as never)), }); - const worker = workerLive.pipe( - Layer.provide(Layer.mergeAll(idAllocatorLayer, continuationRequestsLayer, threads)), + const worker = ProviderContinuationService.workerLive.pipe( + Layer.provide(Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, threads)), ); - return Layer.merge(continuationRequestsLayer, worker); + return Layer.merge(ProviderContinuationRequests.layer, worker); } describe("ProviderContinuationService", () => { @@ -131,7 +127,7 @@ describe("ProviderContinuationService", () => { ], } as unknown as OrchestrationV2ThreadProjection; yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer({ threadId, providerThreadId, @@ -160,7 +156,7 @@ describe("ProviderContinuationService", () => { return Effect.gen(function* () { const dispatched = yield* Queue.unbounded(); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer(request()); const command = (yield* Queue.take(dispatched)) as { readonly creationSource: string; @@ -186,7 +182,7 @@ describe("ProviderContinuationService", () => { return Effect.gen(function* () { const dispatched = yield* Queue.unbounded(); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer({ threadId, providerThreadId, @@ -228,7 +224,7 @@ describe("ProviderContinuationService", () => { return Effect.gen(function* () { const dispatched = yield* Queue.unbounded(); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer({ threadId, providerThreadId, @@ -280,7 +276,7 @@ describe("ProviderContinuationService", () => { return Effect.gen(function* () { const attempts = yield* Ref.make(0); const dispatched = yield* Queue.unbounded(); - const threads = Layer.mock(ThreadManagementService)({ + const threads = Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: () => Effect.succeed(delegatedProjection()), dispatch: (command) => Ref.getAndUpdate(attempts, (count) => count + 1).pipe( @@ -291,12 +287,14 @@ describe("ProviderContinuationService", () => { ), ), }); - const worker = workerLive.pipe( - Layer.provide(Layer.mergeAll(idAllocatorLayer, continuationRequestsLayer, threads)), + const worker = ProviderContinuationService.workerLive.pipe( + Layer.provide( + Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, threads), + ), ); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer({ threadId, providerThreadId, @@ -319,7 +317,10 @@ describe("ProviderContinuationService", () => { }; assert.equal(command.messageId, delegatedMessageId); assert.equal(yield* Ref.get(attempts), 2); - }).pipe(Effect.provide(Layer.merge(continuationRequestsLayer, worker)), Effect.scoped); + }).pipe( + Effect.provide(Layer.merge(ProviderContinuationRequests.layer, worker)), + Effect.scoped, + ); }); }); @@ -327,7 +328,7 @@ describe("ProviderContinuationService", () => { return Effect.gen(function* () { const attempts = yield* Ref.make(0); const disposition = yield* Ref.make<"open" | "disposed">("open"); - const threads = Layer.mock(ThreadManagementService)({ + const threads = Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: () => Ref.get(disposition).pipe(Effect.map((state) => delegatedProjection(state))), dispatch: () => @@ -335,12 +336,14 @@ describe("ProviderContinuationService", () => { Effect.andThen(Effect.fail(new Error("simulated persistent failure") as never)), ), }); - const worker = workerLive.pipe( - Layer.provide(Layer.mergeAll(idAllocatorLayer, continuationRequestsLayer, threads)), + const worker = ProviderContinuationService.workerLive.pipe( + Layer.provide( + Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, threads), + ), ); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer({ threadId, providerThreadId, @@ -368,7 +371,10 @@ describe("ProviderContinuationService", () => { yield* Ref.set(disposition, "disposed"); yield* TestClock.adjust("400 millis"); - }).pipe(Effect.provide(Layer.merge(continuationRequestsLayer, worker)), Effect.scoped); + }).pipe( + Effect.provide(Layer.merge(ProviderContinuationRequests.layer, worker)), + Effect.scoped, + ); }); }); @@ -379,7 +385,7 @@ describe("ProviderContinuationService", () => { const blockedProjectionReads = yield* Ref.make(0); const blockedRequestDropped = yield* Deferred.make(); const state = yield* Ref.make<"open" | "blocked" | "disposed">("open"); - const threads = Layer.mock(ThreadManagementService)({ + const threads = Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: () => Effect.gen(function* () { const currentState = yield* Ref.get(state); @@ -405,8 +411,10 @@ describe("ProviderContinuationService", () => { Effect.andThen(Effect.fail(new Error("simulated persistent failure") as never)), ), }); - const worker = workerLive.pipe( - Layer.provide(Layer.mergeAll(idAllocatorLayer, continuationRequestsLayer, threads)), + const worker = ProviderContinuationService.workerLive.pipe( + Layer.provide( + Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, threads), + ), ); const completionRequest = { threadId, @@ -422,7 +430,7 @@ describe("ProviderContinuationService", () => { }; yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer(completionRequest); yield* Effect.yieldNow; assert.equal(yield* Ref.get(attempts), 1); @@ -446,7 +454,10 @@ describe("ProviderContinuationService", () => { yield* Ref.set(state, "disposed"); yield* TestClock.adjust("200 millis"); - }).pipe(Effect.provide(Layer.merge(continuationRequestsLayer, worker)), Effect.scoped); + }).pipe( + Effect.provide(Layer.merge(ProviderContinuationRequests.layer, worker)), + Effect.scoped, + ); } }); }); @@ -455,7 +466,7 @@ describe("ProviderContinuationService", () => { return Effect.gen(function* () { const attempts = yield* Ref.make(0); const disposition = yield* Ref.make<"open" | "disposed">("open"); - const threads = Layer.mock(ThreadManagementService)({ + const threads = Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: () => Ref.get(disposition).pipe(Effect.map((state) => delegatedProjection(state))), dispatch: () => @@ -463,12 +474,14 @@ describe("ProviderContinuationService", () => { Effect.andThen(Effect.fail(new Error("simulated dispatch failure") as never)), ), }); - const worker = workerLive.pipe( - Layer.provide(Layer.mergeAll(idAllocatorLayer, continuationRequestsLayer, threads)), + const worker = ProviderContinuationService.workerLive.pipe( + Layer.provide( + Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, threads), + ), ); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer({ threadId, providerThreadId, @@ -488,7 +501,10 @@ describe("ProviderContinuationService", () => { yield* TestClock.adjust("100 millis"); yield* Effect.yieldNow; assert.equal(yield* Ref.get(attempts), 1); - }).pipe(Effect.provide(Layer.merge(continuationRequestsLayer, worker)), Effect.scoped); + }).pipe( + Effect.provide(Layer.merge(ProviderContinuationRequests.layer, worker)), + Effect.scoped, + ); }); }); @@ -496,7 +512,7 @@ describe("ProviderContinuationService", () => { return Effect.gen(function* () { const dispatched = yield* Queue.unbounded(); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer({ threadId, providerThreadId, @@ -532,7 +548,7 @@ describe("ProviderContinuationService", () => { for (const disposition of ["stopped", "disposed"] as const) { const dispatched = yield* Queue.unbounded(); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer({ threadId, providerThreadId, @@ -566,7 +582,7 @@ describe("ProviderContinuationService", () => { return Effect.gen(function* () { const dispatched = yield* Queue.unbounded(); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer({ threadId, providerThreadId, @@ -603,7 +619,7 @@ describe("ProviderContinuationService", () => { for (const barrier of ["archivedAt", "deletedAt"] as const) { const dispatched = yield* Queue.unbounded(); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer({ threadId, providerThreadId, @@ -643,7 +659,7 @@ describe("ProviderContinuationService", () => { return Effect.gen(function* () { const dispatched = yield* Queue.unbounded(); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer(request()); yield* Queue.take(dispatched); yield* Effect.yieldNow; @@ -661,7 +677,7 @@ describe("ProviderContinuationService", () => { return Effect.gen(function* () { const dispatched = yield* Queue.unbounded(); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer(request()); const command = yield* Queue.take(dispatched); assert.deepEqual((command as { readonly dispatchMode?: unknown }).dispatchMode, { @@ -680,7 +696,7 @@ describe("ProviderContinuationService", () => { return Effect.gen(function* () { const dispatched = yield* Queue.unbounded(); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; const guard = yield* makeGuard(); const dispatchIfCurrent = yield* guard.capture; yield* guard.invalidate; @@ -704,7 +720,7 @@ describe("ProviderContinuationService", () => { const releaseProjection = yield* Deferred.make(); const guardCompleted = yield* Deferred.make(); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; const guard = yield* makeGuard(guardCompleted); const dispatchIfCurrent = yield* guard.capture; yield* requests.offer(request(dispatchIfCurrent)); @@ -736,7 +752,7 @@ describe("ProviderContinuationService", () => { const releaseFirstProjection = yield* Deferred.make(); let projectionCalls = 0; yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; const guard = yield* makeGuard(); const first = yield* guard.capture; yield* requests.offer(request(first, "A")); @@ -773,7 +789,7 @@ describe("ProviderContinuationService", () => { const dispatched = yield* Queue.unbounded(); const cleared = yield* Ref.make(false); yield* Effect.gen(function* () { - const requests = yield* ProviderContinuationRequests; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; yield* requests.offer({ ...request(), clearIfCurrent: () => Ref.set(cleared, true), diff --git a/apps/server/src/orchestration-v2/ProviderContinuationService.ts b/apps/server/src/orchestration-v2/ProviderContinuationService.ts index f45e30c7a950..3384e6a6dff9 100644 --- a/apps/server/src/orchestration-v2/ProviderContinuationService.ts +++ b/apps/server/src/orchestration-v2/ProviderContinuationService.ts @@ -3,12 +3,9 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Ref from "effect/Ref"; -import { IdAllocatorV2 } from "./IdAllocator.ts"; -import { - type ProviderContinuationRequest, - ProviderContinuationRequests, -} from "./ProviderContinuationRequests.ts"; -import { ThreadManagementService } from "./ThreadManagementService.ts"; +import * as IdAllocator from "./IdAllocator.ts"; +import * as ProviderContinuationRequests from "./ProviderContinuationRequests.ts"; +import * as ThreadManagementService from "./ThreadManagementService.ts"; import { isUndeliveredMailboxSteer } from "./NotificationMailbox.ts"; const CONTINUATION_MESSAGE_TEXT = "Background task completed."; @@ -22,7 +19,9 @@ function delegatedCompletionText(taskIds: ReadonlyArray): string { function currentDelegatedCompletionDelivery( projection: Pick, - completion: NonNullable, + completion: NonNullable< + ProviderContinuationRequests.ProviderContinuationRequest["delegatedCompletion"] + >, ) { const sourceRun = projection.runs.find((candidate) => candidate.id === completion.parentRunId); const delivery = sourceRun?.delegatedCompletion?.delivery; @@ -43,8 +42,10 @@ function currentDelegatedCompletionDelivery( } function delegatedCompletionRetryKey( - request: ProviderContinuationRequest, - completion: NonNullable, + request: ProviderContinuationRequests.ProviderContinuationRequest, + completion: NonNullable< + ProviderContinuationRequests.ProviderContinuationRequest["delegatedCompletion"] + >, ): string { return `${request.threadId}:${completion.parentRunId}:${completion.generation}:${completion.messageId}`; } @@ -58,9 +59,9 @@ function delegatedCompletionRetryKey( */ export const workerLive = Layer.effectDiscard( Effect.gen(function* () { - const ids = yield* IdAllocatorV2; - const requests = yield* ProviderContinuationRequests; - const threads = yield* ThreadManagementService; + const ids = yield* IdAllocator.IdAllocatorV2; + const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; + const threads = yield* ThreadManagementService.ThreadManagementService; const retryAttempts = yield* Ref.make(new Map()); const clearRetryAttempt = (key: string) => @@ -80,7 +81,7 @@ export const workerLive = Layer.effectDiscard( }); const dispatchContinuation = Effect.fn("ProviderContinuationService.dispatchContinuation")( - function* (request: ProviderContinuationRequest) { + function* (request: ProviderContinuationRequests.ProviderContinuationRequest) { const projection = yield* threads.getThreadRecords( request.threadId, ["messages", "runs", "providerTurns"], diff --git a/apps/server/src/orchestration-v2/ProviderEventIngestor.test.ts b/apps/server/src/orchestration-v2/ProviderEventIngestor.test.ts index eb4f91daf7ce..f1470125a889 100644 --- a/apps/server/src/orchestration-v2/ProviderEventIngestor.test.ts +++ b/apps/server/src/orchestration-v2/ProviderEventIngestor.test.ts @@ -28,19 +28,11 @@ import * as Stream from "effect/Stream"; import * as TestClock from "effect/testing/TestClock"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; -import { EventSinkV2, layer as eventSinkLayer } from "./EventSink.ts"; -import { EventStoreV2, layer as eventStoreLayer } from "./EventStore.ts"; -import { - IdAllocatorV2, - type IdAllocatorV2Error, - layer as idAllocatorLayer, -} from "./IdAllocator.ts"; -import { ProjectionStoreV2, layer as projectionStoreLayer } from "./ProjectionStore.ts"; -import { - ProviderEventIngestorV2, - ProviderTurnAnalytics, - layer as providerEventIngestorLayer, -} from "./ProviderEventIngestor.ts"; +import * as EventSink from "./EventSink.ts"; +import * as EventStore from "./EventStore.ts"; +import * as IdAllocator from "./IdAllocator.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; +import * as ProviderEventIngestor from "./ProviderEventIngestor.ts"; import { makeProviderFailure } from "./ProviderFailure.ts"; import { makeProviderEventRoutingState, @@ -50,20 +42,20 @@ import { } from "./RunExecutionService.ts"; const TestDatabaseLayer = SqlitePersistenceMemory; -const TestStoresLayer = Layer.merge(eventStoreLayer, projectionStoreLayer).pipe( +const TestStoresLayer = Layer.merge(EventStore.layer, ProjectionStore.layer).pipe( Layer.provide(TestDatabaseLayer), ); -const TestEventSinkLayer = eventSinkLayer.pipe( +const TestEventSinkLayer = EventSink.layer.pipe( Layer.provide(Layer.mergeAll(TestStoresLayer, TestDatabaseLayer)), ); const TestLayer = Layer.mergeAll( TestStoresLayer, TestEventSinkLayer, - idAllocatorLayer, - providerEventIngestorLayer.pipe( - Layer.provide(Layer.mergeAll(TestStoresLayer, TestEventSinkLayer, idAllocatorLayer)), + IdAllocator.layer, + ProviderEventIngestor.layer.pipe( + Layer.provide(Layer.mergeAll(TestStoresLayer, TestEventSinkLayer, IdAllocator.layer)), ), ); const modelSelection = { @@ -74,9 +66,13 @@ const CODEX_DRIVER = ProviderDriverKind.make("codex"); function threadCreatedEvent( now: DateTime.Utc, -): Effect.Effect { +): Effect.Effect< + OrchestrationV2DomainEvent, + IdAllocator.IdAllocatorV2Error, + IdAllocator.IdAllocatorV2 +> { return Effect.gen(function* () { - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-event-ingestor", }); @@ -132,7 +128,7 @@ const layer = it.layer(TestLayer); it.effect("records accepted billed turn usage once without billing the context window", () => { const recorded: Array>> = []; - const analytics = Layer.succeed(ProviderTurnAnalytics, { + const analytics = Layer.succeed(ProviderEventIngestor.ProviderTurnAnalytics, { record: (properties: Readonly>) => Effect.sync(() => { recorded.push(properties); @@ -140,9 +136,9 @@ it.effect("records accepted billed turn usage once without billing the context w }); return Effect.gen(function* () { const now = yield* DateTime.now; - const eventSink = yield* EventSinkV2; - const ingestor = yield* ProviderEventIngestorV2; - const idAllocator = yield* IdAllocatorV2; + const eventSink = yield* EventSink.EventSinkV2; + const ingestor = yield* ProviderEventIngestor.ProviderEventIngestorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const threadEvent = yield* threadCreatedEvent(now); yield* eventSink.write({ events: [threadEvent] }); const providerSessionId = yield* idAllocator.allocate.providerSession({ @@ -234,11 +230,11 @@ layer("ProviderEventIngestorV2", (it) => { it.effect("normalizes provider events through the real event log and projection store", () => Effect.gen(function* () { const now = yield* DateTime.now; - const eventSink = yield* EventSinkV2; - const eventStore = yield* EventStoreV2; - const projectionStore = yield* ProjectionStoreV2; - const ingestor = yield* ProviderEventIngestorV2; - const idAllocator = yield* IdAllocatorV2; + const eventSink = yield* EventSink.EventSinkV2; + const eventStore = yield* EventStore.EventStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; + const ingestor = yield* ProviderEventIngestor.ProviderEventIngestorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const threadEvent = yield* threadCreatedEvent(now); const providerSessionId = yield* idAllocator.allocate.providerSession({ providerInstanceId: modelSelection.instanceId, @@ -316,10 +312,10 @@ layer("ProviderEventIngestorV2", (it) => { Effect.gen(function* () { yield* TestClock.setTime(Date.parse("2026-09-07T00:00:00.000Z")); const now = yield* DateTime.now; - const eventSink = yield* EventSinkV2; - const projectionStore = yield* ProjectionStoreV2; - const ingestor = yield* ProviderEventIngestorV2; - const idAllocator = yield* IdAllocatorV2; + const eventSink = yield* EventSink.EventSinkV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; + const ingestor = yield* ProviderEventIngestor.ProviderEventIngestorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const threadEvent = yield* threadCreatedEvent(now); const providerSessionId = yield* idAllocator.allocate.providerSession({ providerInstanceId: modelSelection.instanceId, @@ -337,7 +333,10 @@ layer("ProviderEventIngestorV2", (it) => { status: "active", steps, }); - const ingest = (service: ProviderEventIngestorV2["Service"], steps: TodoListPlan["steps"]) => + const ingest = ( + service: ProviderEventIngestor.ProviderEventIngestorV2["Service"], + steps: TodoListPlan["steps"], + ) => service.ingestNormalized({ providerSessionId, providerInstanceId: modelSelection.instanceId, @@ -358,11 +357,11 @@ layer("ProviderEventIngestorV2", (it) => { { id: "fallback", text: "Report", status: "pending" }, ]); - const restartedIngestor = yield* ProviderEventIngestorV2.pipe( + const restartedIngestor = yield* ProviderEventIngestor.ProviderEventIngestorV2.pipe( Effect.provide( - Layer.fresh(providerEventIngestorLayer).pipe( + Layer.fresh(ProviderEventIngestor.layer).pipe( Layer.provide( - Layer.succeed(ProjectionStoreV2, { + Layer.succeed(ProjectionStore.ProjectionStoreV2, { ...projectionStore, getThreadProjection: () => Effect.die("Plan timing must not load thread history"), }), @@ -429,8 +428,8 @@ layer("ProviderEventIngestorV2", (it) => { "treats successful provider terminal markers as non-persisted orchestration control signals", () => Effect.gen(function* () { - const ingestor = yield* ProviderEventIngestorV2; - const idAllocator = yield* IdAllocatorV2; + const ingestor = yield* ProviderEventIngestor.ProviderEventIngestorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-event-terminal", }); @@ -471,10 +470,10 @@ layer("ProviderEventIngestorV2", (it) => { it.effect("persists an interrupted run's inherited terminal through the live run router", () => Effect.gen(function* () { const now = yield* DateTime.now; - const eventSink = yield* EventSinkV2; - const projectionStore = yield* ProjectionStoreV2; - const ingestor = yield* ProviderEventIngestorV2; - const idAllocator = yield* IdAllocatorV2; + const eventSink = yield* EventSink.EventSinkV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; + const ingestor = yield* ProviderEventIngestor.ProviderEventIngestorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const threadEvent = yield* threadCreatedEvent(now); const priorRunId = RunId.make("run:provider-event-inherited:prior"); const currentRunId = RunId.make("run:provider-event-inherited:current"); @@ -585,10 +584,10 @@ layer("ProviderEventIngestorV2", (it) => { it.effect("persists a completed run's late background terminal exactly once", () => Effect.gen(function* () { const now = yield* DateTime.now; - const eventSink = yield* EventSinkV2; - const eventStore = yield* EventStoreV2; - const ingestor = yield* ProviderEventIngestorV2; - const idAllocator = yield* IdAllocatorV2; + const eventSink = yield* EventSink.EventSinkV2; + const eventStore = yield* EventStore.EventStoreV2; + const ingestor = yield* ProviderEventIngestor.ProviderEventIngestorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const threadEvent = yield* threadCreatedEvent(now); const priorRunId = RunId.make("run:provider-event-completed:prior"); const currentRunId = RunId.make("run:provider-event-completed:current"); @@ -729,11 +728,11 @@ layer("ProviderEventIngestorV2", (it) => { it.effect(`dismisses only native questions when a provider turn ends with ${terminal}`, () => Effect.gen(function* () { const now = yield* DateTime.now; - const eventSink = yield* EventSinkV2; - const eventStore = yield* EventStoreV2; - const projectionStore = yield* ProjectionStoreV2; - const ingestor = yield* ProviderEventIngestorV2; - const idAllocator = yield* IdAllocatorV2; + const eventSink = yield* EventSink.EventSinkV2; + const eventStore = yield* EventStore.EventStoreV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; + const ingestor = yield* ProviderEventIngestor.ProviderEventIngestorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const threadEvent = yield* threadCreatedEvent(now); const threadId = threadEvent.threadId; const providerSessionId = yield* idAllocator.allocate.providerSession({ @@ -918,10 +917,10 @@ layer("ProviderEventIngestorV2", (it) => { () => Effect.scoped( Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const eventStore = yield* EventStoreV2; - const projections = yield* ProjectionStoreV2; - const idAllocator = yield* IdAllocatorV2; + const eventSink = yield* EventSink.EventSinkV2; + const eventStore = yield* EventStore.EventStoreV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const now = yield* DateTime.now; const threadEvent = yield* threadCreatedEvent(now); const threadId = threadEvent.threadId; @@ -1000,7 +999,7 @@ layer("ProviderEventIngestorV2", (it) => { yield* eventSink.write({ events: seedEvents }); const normalized = yield* Deferred.make(); const releaseTerminalWrite = yield* Deferred.make(); - const gatedSink = EventSinkV2.of({ + const gatedSink = EventSink.EventSinkV2.of({ ...eventSink, write: (input) => Deferred.succeed(normalized, undefined).pipe( @@ -1008,9 +1007,9 @@ layer("ProviderEventIngestorV2", (it) => { Effect.andThen(eventSink.write(input)), ), }); - const ingestor = yield* ProviderEventIngestorV2.pipe( - Effect.provide(Layer.fresh(providerEventIngestorLayer)), - Effect.provideService(EventSinkV2, gatedSink), + const ingestor = yield* ProviderEventIngestor.ProviderEventIngestorV2.pipe( + Effect.provide(Layer.fresh(ProviderEventIngestor.layer)), + Effect.provideService(EventSink.EventSinkV2, gatedSink), ); const terminal = yield* ingestor .ingestNormalized({ @@ -1089,10 +1088,10 @@ layer("ProviderEventIngestorV2", (it) => { Effect.gen(function* () { const now = yield* DateTime.now; const retryStartedAt = DateTime.makeUnsafe(DateTime.toEpochMillis(now) - 5_000); - const eventSink = yield* EventSinkV2; - const projectionStore = yield* ProjectionStoreV2; - const ingestor = yield* ProviderEventIngestorV2; - const idAllocator = yield* IdAllocatorV2; + const eventSink = yield* EventSink.EventSinkV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; + const ingestor = yield* ProviderEventIngestor.ProviderEventIngestorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const threadEvent = yield* threadCreatedEvent(now); const providerSessionId = yield* idAllocator.allocate.providerSession({ providerInstanceId: modelSelection.instanceId, @@ -1164,8 +1163,8 @@ layer("ProviderEventIngestorV2", (it) => { it.effect("routes provider-owned child artifacts to their child app thread", () => Effect.gen(function* () { const now = yield* DateTime.now; - const ingestor = yield* ProviderEventIngestorV2; - const idAllocator = yield* IdAllocatorV2; + const ingestor = yield* ProviderEventIngestor.ProviderEventIngestorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const rootEvent = yield* threadCreatedEvent(now); if (rootEvent.type !== "thread.created") { throw new Error("Expected a thread.created fixture event"); diff --git a/apps/server/src/orchestration-v2/ProviderEventIngestor.ts b/apps/server/src/orchestration-v2/ProviderEventIngestor.ts index e6764f1e9146..8082962b86de 100644 --- a/apps/server/src/orchestration-v2/ProviderEventIngestor.ts +++ b/apps/server/src/orchestration-v2/ProviderEventIngestor.ts @@ -25,10 +25,10 @@ import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; import { getModelSelectionStringOptionValue } from "@t3tools/shared/model"; -import { AnalyticsService } from "../telemetry/AnalyticsService.ts"; -import { EventSinkV2 } from "./EventSink.ts"; -import { ProjectionStoreV2 } from "./ProjectionStore.ts"; -import { IdAllocatorV2 } from "./IdAllocator.ts"; +import * as AnalyticsService from "../telemetry/AnalyticsService.ts"; +import * as EventSink from "./EventSink.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; +import * as IdAllocator from "./IdAllocator.ts"; import { ProviderAdapterV2Event } from "./ProviderAdapter.ts"; import { makeProviderFailureTurnItem } from "./ProviderFailure.ts"; @@ -80,7 +80,7 @@ export class ProviderTurnAnalytics extends Context.Reference<{ export const analyticsLive = Layer.effect( ProviderTurnAnalytics, Effect.gen(function* () { - const analytics = yield* AnalyticsService; + const analytics = yield* AnalyticsService.AnalyticsService; return { record: (properties: Readonly>) => analytics.record("provider.turn.completed", properties), @@ -249,13 +249,13 @@ const decodeDomainEvent = Schema.decodeUnknownEffect(OrchestrationV2DomainEvent) export const layer: Layer.Layer< ProviderEventIngestorV2, never, - EventSinkV2 | IdAllocatorV2 | ProjectionStoreV2 + EventSink.EventSinkV2 | IdAllocator.IdAllocatorV2 | ProjectionStore.ProjectionStoreV2 > = Layer.effect( ProviderEventIngestorV2, Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const projections = yield* ProjectionStoreV2; - const idAllocator = yield* IdAllocatorV2; + const eventSink = yield* EventSink.EventSinkV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const analytics = yield* ProviderTurnAnalytics; const completedTurnAnalytics = new Set(); diff --git a/apps/server/src/orchestration-v2/ProviderFailure.test.ts b/apps/server/src/orchestration-v2/ProviderFailure.test.ts index c63765c8715a..f05d3e91cd8a 100644 --- a/apps/server/src/orchestration-v2/ProviderFailure.test.ts +++ b/apps/server/src/orchestration-v2/ProviderFailure.test.ts @@ -17,7 +17,7 @@ import { MAX_PROVIDER_FAILURE_CODE_LENGTH, MAX_PROVIDER_FAILURE_MESSAGE_LENGTH, } from "./ProviderFailure.ts"; -import { IdAllocatorV2, layer as idAllocatorLayer } from "./IdAllocator.ts"; +import * as IdAllocator from "./IdAllocator.ts"; import { ContextHandoffBudgetError } from "./ContextHandoffDelivery.ts"; import { ProviderAdapterTurnStartError } from "./ProviderAdapter.ts"; @@ -170,7 +170,7 @@ it("does not serialize arbitrary provider causes", () => { it.effect("keys terminal failure items by provider turn across retries and fallback paths", () => Effect.gen(function* () { - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const driver = ProviderDriverKind.make("codex"); const runId = RunId.make("run:provider-failure-id"); const base = { @@ -205,5 +205,5 @@ it.effect("keys terminal failure items by provider turn across retries and fallb assert.notEqual(firstAttempt.id, retriedAttempt.id); assert.equal(firstAttempt.id, ingestorFallback.id); assert.equal(firstAttempt.ordinal, 101); - }).pipe(Effect.provide(idAllocatorLayer)), + }).pipe(Effect.provide(IdAllocator.layer)), ); diff --git a/apps/server/src/orchestration-v2/ProviderRuntimeRecoveryService.ts b/apps/server/src/orchestration-v2/ProviderRuntimeRecoveryService.ts index cbcade57fed0..67ae22f1883b 100644 --- a/apps/server/src/orchestration-v2/ProviderRuntimeRecoveryService.ts +++ b/apps/server/src/orchestration-v2/ProviderRuntimeRecoveryService.ts @@ -17,8 +17,7 @@ import * as EffectOutbox from "./EffectOutbox.ts"; import * as EventSink from "./EventSink.ts"; import * as IdAllocator from "./IdAllocator.ts"; import * as ProjectionStore from "./ProjectionStore.ts"; -import type { ProjectionRuntimeRecoveryState } from "./ProjectionStore.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; +import * as ServerSettings from "../serverSettings.ts"; import { restartContinuationRun } from "./RestartContinuation.ts"; import { cancelledRosterTaskWork, @@ -66,7 +65,7 @@ export class ProviderRuntimeRecoveryService extends Context.Service< } >()("t3/orchestration-v2/ProviderRuntimeRecoveryService") {} -function nonterminalRuns(projection: ProjectionRuntimeRecoveryState) { +function nonterminalRuns(projection: ProjectionStore.ProjectionRuntimeRecoveryState) { return projection.runs.filter((run) => { const status: string = run.status; return ( @@ -108,7 +107,7 @@ function providerThreadHasPendingBackgroundTasks( */ function resolveStaleBackgroundItemProviderInstanceId( item: OrchestrationV2ThreadProjection["turnItems"][number], - projection: ProjectionRuntimeRecoveryState, + projection: ProjectionStore.ProjectionRuntimeRecoveryState, ): OrchestrationV2ThreadProjection["thread"]["providerInstanceId"] { if (item.runId !== null) { const run = projection.runs.find((candidate) => candidate.id === item.runId); @@ -135,7 +134,7 @@ function resolveStaleBackgroundItemProviderInstanceId( * record for their next turn. */ function providerThreadsWithOpenBackgroundWork( - projection: ProjectionRuntimeRecoveryState, + projection: ProjectionStore.ProjectionRuntimeRecoveryState, ): ReadonlySet { const ids = new Set(); for (const item of projection.turnItems ?? []) { @@ -159,7 +158,7 @@ function providerThreadsWithOpenBackgroundWork( * the next run on the same provider thread delivers it with its input. */ function latestStartedRun( - projection: ProjectionRuntimeRecoveryState, + projection: ProjectionStore.ProjectionRuntimeRecoveryState, providerThreadId: ProviderThreadId, ) { return projection.runs.reduce( @@ -175,14 +174,14 @@ function latestStartedRun( } export const make = Effect.gen(function* () { - const settings = yield* ServerSettingsService; + const settings = yield* ServerSettings.ServerSettingsService; const projections = yield* ProjectionStore.ProjectionStoreV2; const eventSink = yield* EventSink.EventSinkV2; const ids = yield* IdAllocator.IdAllocatorV2; const outbox = yield* EffectOutbox.EffectOutboxV2; const reconcileProjection = Effect.fn("ProviderRuntimeRecoveryService.reconcileProjection")( function* ( - projection: ProjectionRuntimeRecoveryState, + projection: ProjectionStore.ProjectionRuntimeRecoveryState, trigger: "startup" | "shutdown", continueAfterRestart: boolean, ) { diff --git a/apps/server/src/orchestration-v2/ProviderSessionManager.test.ts b/apps/server/src/orchestration-v2/ProviderSessionManager.test.ts index 6e6f800943f3..63d4c57fb663 100644 --- a/apps/server/src/orchestration-v2/ProviderSessionManager.test.ts +++ b/apps/server/src/orchestration-v2/ProviderSessionManager.test.ts @@ -31,21 +31,17 @@ import { TestClock } from "effect/testing"; import { HttpServer } from "effect/unstable/http"; import { ProviderWorkspaceMissingError } from "../provider/Errors.ts"; -import { ServerEnvironment } from "../environment/ServerEnvironment.ts"; +import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; import * as ProjectService from "../project/ProjectService.ts"; import * as McpProviderSession from "../mcp/McpProviderSession.ts"; import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; import * as ServerSettings from "../serverSettings.ts"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; -import { EventSinkV2, EventSinkWriteError, layer as eventSinkLayer } from "./EventSink.ts"; -import { layer as eventStoreLayer } from "./EventStore.ts"; -import { - IdAllocatorV2, - type IdAllocatorV2Shape, - layer as idAllocatorLayer, -} from "./IdAllocator.ts"; -import { ProjectionStoreV2, layer as projectionStoreLayer } from "./ProjectionStore.ts"; +import * as EventSink from "./EventSink.ts"; +import * as EventStore from "./EventStore.ts"; +import * as IdAllocator from "./IdAllocator.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; import { ProviderAdapterEventStreamError, type ProviderAdapterV2Event, @@ -54,25 +50,22 @@ import { type ProviderAdapterV2SessionRuntime, type ProviderAdapterV2Shape, } from "./ProviderAdapter.ts"; -import { makeSingleLayer as makeProviderAdapterRegistryLayer } from "./ProviderAdapterRegistry.ts"; -import { layer as providerEventIngestorLayer } from "./ProviderEventIngestor.ts"; -import { - ProviderSessionManagerV2, - layerWithOptions as providerSessionManagerLayerWithOptions, -} from "./ProviderSessionManager.ts"; +import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; +import * as ProviderEventIngestor from "./ProviderEventIngestor.ts"; +import * as ProviderSessionManager from "./ProviderSessionManager.ts"; const TestDatabaseLayer = SqlitePersistenceMemory; -const TestStoresLayer = Layer.merge(eventStoreLayer, projectionStoreLayer).pipe( +const TestStoresLayer = Layer.merge(EventStore.layer, ProjectionStore.layer).pipe( Layer.provide(TestDatabaseLayer), ); -const TestEventSinkLayer = eventSinkLayer.pipe( +const TestEventSinkLayer = EventSink.layer.pipe( Layer.provide(Layer.mergeAll(TestStoresLayer, TestDatabaseLayer)), ); const FailingReleaseEventSinkLayer = Layer.effect( - EventSinkV2, + EventSink.EventSinkV2, Effect.gen(function* () { - const delegate = yield* EventSinkV2; - return EventSinkV2.of({ + const delegate = yield* EventSink.EventSinkV2; + return EventSink.EventSinkV2.of({ ...delegate, write: (input) => input.events.some( @@ -80,7 +73,7 @@ const FailingReleaseEventSinkLayer = Layer.effect( event.type === "provider-session.updated" && (event.payload.status === "stopped" || event.payload.status === "error"), ) - ? Effect.fail(new EventSinkWriteError({ eventCount: input.events.length })) + ? Effect.fail(new EventSink.EventSinkWriteError({ eventCount: input.events.length })) : delegate.write(input), }); }), @@ -145,7 +138,7 @@ function makeProviderSession(input: { } function makeThreadCreatedEvent(input: { - readonly idAllocator: IdAllocatorV2Shape; + readonly idAllocator: IdAllocator.IdAllocatorV2Shape; readonly threadId: ThreadId; readonly now: DateTime.Utc; readonly projectId?: ProjectId; @@ -198,7 +191,7 @@ function makeThreadCreatedEvent(input: { } function makeProviderThread(input: { - readonly idAllocator: IdAllocatorV2Shape; + readonly idAllocator: IdAllocator.IdAllocatorV2Shape; readonly threadId: ThreadId; readonly providerSessionId: ProviderSessionId; readonly now: DateTime.Utc; @@ -374,7 +367,7 @@ function makeTestLayer(input: { const configuredEventSinkLayer = input.failReleaseEventWrites ? FailingReleaseEventSinkLayer : TestEventSinkLayer; - const registryLayer = makeProviderAdapterRegistryLayer( + const registryLayer = ProviderAdapterRegistry.makeSingleLayer( makeProviderAdapter(input.state, { failEventStream: input.failEventStream ?? false, ...(input.capabilities === undefined ? {} : { capabilities: input.capabilities }), @@ -389,15 +382,15 @@ function makeTestLayer(input: { ...(input.beforeUnload === undefined ? {} : { beforeUnload: input.beforeUnload }), }), ); - const providerEventIngestorTestLayer = providerEventIngestorLayer.pipe( - Layer.provide(Layer.mergeAll(configuredEventSinkLayer, idAllocatorLayer, TestStoresLayer)), + const providerEventIngestorTestLayer = ProviderEventIngestor.layer.pipe( + Layer.provide(Layer.mergeAll(configuredEventSinkLayer, IdAllocator.layer, TestStoresLayer)), ); return Layer.mergeAll( TestStoresLayer, configuredEventSinkLayer, - idAllocatorLayer, + IdAllocator.layer, TestMcpRegistryLayer, - providerSessionManagerLayerWithOptions({ + ProviderSessionManager.layerWithOptions({ idleTimeoutMs: input.idleTimeoutMs, ...(input.maxIdlePinMs === undefined ? {} : { maxIdlePinMs: input.maxIdlePinMs }), }).pipe( @@ -405,7 +398,7 @@ function makeTestLayer(input: { Layer.mergeAll( registryLayer, configuredEventSinkLayer, - idAllocatorLayer, + IdAllocator.layer, providerEventIngestorTestLayer, TestMcpRegistryLayer, TestStoresLayer, @@ -422,7 +415,7 @@ const fakeHttpServer = HttpServer.HttpServer.of({ serve: (() => Effect.void) as HttpServer.HttpServer["Service"]["serve"], }); -const fakeEnvironment = ServerEnvironment.of({ +const fakeEnvironment = ServerEnvironment.ServerEnvironment.of({ getEnvironmentId: Effect.succeed(EnvironmentId.make("environment-provider-session-manager")), getDescriptor: Effect.die("unused"), }); @@ -432,7 +425,7 @@ const TestMcpRegistryLayer = Layer.effect( McpSessionRegistry.__testing.make(), ).pipe( Layer.provide(Layer.succeed(HttpServer.HttpServer, fakeHttpServer)), - Layer.provide(Layer.succeed(ServerEnvironment, fakeEnvironment)), + Layer.provide(Layer.succeed(ServerEnvironment.ServerEnvironment, fakeEnvironment)), Layer.provide(NodeServices.layer), ); @@ -478,9 +471,9 @@ function runBrowserAccessScenario(input: { }); yield* Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const now = yield* DateTime.now; const providerSessionId = yield* idAllocator.allocate.providerSession({ providerInstanceId: modelSelection.instanceId, @@ -521,7 +514,7 @@ function runBrowserAccessScenario(input: { } function makePendingRuntimeRequestEvents(input: { - readonly idAllocator: IdAllocatorV2Shape; + readonly idAllocator: IdAllocator.IdAllocatorV2Shape; readonly threadId: ThreadId; readonly providerSessionId: ProviderSessionId; readonly providerThread: OrchestrationV2ProviderThread; @@ -662,9 +655,9 @@ it.effect("ProviderSessionManagerV2 opens independent sessions concurrently", () }); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const now = yield* DateTime.now; const firstThreadId = ThreadId.make("thread-provider-session-manager-concurrent-a"); const secondThreadId = ThreadId.make("thread-provider-session-manager-concurrent-b"); @@ -728,9 +721,9 @@ it.effect("ProviderSessionManagerV2 closes every live session for a provider ins Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const now = yield* DateTime.now; const firstThreadId = ThreadId.make("thread-provider-session-manager-logout-a"); const secondThreadId = ThreadId.make("thread-provider-session-manager-logout-b"); @@ -793,9 +786,9 @@ it.effect("ProviderSessionManagerV2 opens a duplicate session only once", () => ); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread-provider-session-manager-single-flight"); const providerSessionId = yield* idAllocator.allocate.providerSession({ @@ -844,9 +837,9 @@ it.effect("ProviderSessionManagerV2 releases live sessions when its layer shuts Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread-provider-session-manager-shutdown"); const providerSessionId = yield* idAllocator.allocate.providerSession({ @@ -886,9 +879,9 @@ it.effect("ProviderSessionManagerV2 closes event subscriptions normally on serve Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread-provider-session-manager-shutdown-subscription"); const providerSessionId = yield* idAllocator.allocate.providerSession({ @@ -928,9 +921,9 @@ it.effect("ProviderSessionManagerV2 drains subscribers when the provider stops", Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread-provider-session-manager-provider-stop"); const providerSessionId = yield* idAllocator.allocate.providerSession({ @@ -1001,9 +994,9 @@ it.effect( ReadonlyArray >([]); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const registry = yield* McpSessionRegistry.McpSessionRegistry; const now = yield* DateTime.now; const threadId = ThreadId.make("thread-provider-session-manager-mcp"); @@ -1062,9 +1055,9 @@ it.effect( ReadonlyArray >([]); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const registry = yield* McpSessionRegistry.McpSessionRegistry; const now = yield* DateTime.now; const threadId = ThreadId.make("thread-provider-session-manager-no-browser"); @@ -1166,9 +1159,9 @@ it.effect("ProviderSessionManagerV2 revokes MCP credentials when release persist ReadonlyArray >([]); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const registry = yield* McpSessionRegistry.McpSessionRegistry; const now = yield* DateTime.now; const threadId = ThreadId.make("thread-provider-session-manager-mcp-release-failure"); @@ -1218,9 +1211,9 @@ it.effect("ProviderSessionManagerV2 duplicate detach preserves replacement MCP c ReadonlyArray >([]); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const registry = yield* McpSessionRegistry.McpSessionRegistry; const now = yield* DateTime.now; const threadId = ThreadId.make("thread-provider-session-manager-replacement-mcp"); @@ -1290,9 +1283,9 @@ it.effect( ReadonlyArray >([]); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const registry = yield* McpSessionRegistry.McpSessionRegistry; const now = yield* DateTime.now; const threadId = ThreadId.make("thread-provider-session-manager-superseded-mcp"); @@ -1366,9 +1359,9 @@ it.effect( ReadonlyArray >([]); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const registry = yield* McpSessionRegistry.McpSessionRegistry; const now = yield* DateTime.now; const threadId = ThreadId.make("thread-provider-session-manager-stable-mcp"); @@ -1444,9 +1437,9 @@ it.effect( ReadonlyArray >([]); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const registry = yield* McpSessionRegistry.McpSessionRegistry; const now = yield* DateTime.now; const threadId = ThreadId.make("thread-provider-session-manager-stale-record"); @@ -1501,9 +1494,9 @@ it.effect( >([]); const duringOpen = yield* Ref.make>(Effect.void); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const registry = yield* McpSessionRegistry.McpSessionRegistry; const now = yield* DateTime.now; const threadId = ThreadId.make("thread-provider-session-manager-open-race"); @@ -1573,9 +1566,9 @@ it.effect("ProviderSessionManagerV2 terminal detach revokes the thread's MCP cre ReadonlyArray >([]); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const registry = yield* McpSessionRegistry.McpSessionRegistry; const now = yield* DateTime.now; const threadId = ThreadId.make("thread-provider-session-manager-terminal-detach"); @@ -1612,10 +1605,10 @@ it.effect("ProviderSessionManagerV2 releases idle sessions without sweeping all Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-session-manager-idle", @@ -1660,10 +1653,10 @@ it.effect("ProviderSessionManagerV2 persists release when session scope close ha Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const threadId = yield* idAllocator.allocate.thread({ fixtureName: "provider-session-manager-hung-close", @@ -1708,9 +1701,9 @@ it.effect("ProviderSessionManagerV2 defers idle release while background work is const state = yield* Ref.make(emptyState); const pendingWork = yield* Ref.make(true); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const now = yield* DateTime.now; const threadId = yield* idAllocator.allocate.thread({ fixtureName: "provider-session-manager-idle-pin", @@ -1761,9 +1754,9 @@ it.effect("ProviderSessionManagerV2 releases pinned idle sessions once the pin c Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const now = yield* DateTime.now; const threadId = yield* idAllocator.allocate.thread({ fixtureName: "provider-session-manager-pin-cap", @@ -1818,10 +1811,10 @@ it.effect( const checkEntered = yield* Deferred.make(); const checkGate = yield* Deferred.make(); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-session-manager-busy-during-check", @@ -1945,9 +1938,9 @@ it.effect("ProviderSessionManagerV2 does not apply a stale idle pin to a replace const checkEntered = yield* Deferred.make(); const checkGate = yield* Deferred.make(); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const now = yield* DateTime.now; const threadId = yield* idAllocator.allocate.thread({ fixtureName: "provider-session-manager-stale-pin", @@ -2037,10 +2030,10 @@ it.effect( Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-session-manager-active", @@ -2132,10 +2125,10 @@ it.effect("ProviderSessionManagerV2 uses the same release path for runtime failu Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-session-manager-runtime-error", @@ -2182,10 +2175,10 @@ it.effect("ProviderSessionManagerV2 releases sessions when provider event stream Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-session-manager-stream-error", @@ -2236,10 +2229,10 @@ it.effect("ProviderSessionManagerV2 marks pending runtime requests non-live on r Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-session-manager-request-expire", @@ -2302,10 +2295,10 @@ it.effect("ProviderSessionManagerV2 terminalizes a pending input transcript item Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-session-manager-request-expire", @@ -2375,10 +2368,10 @@ it.effect("ProviderSessionManagerV2 persists session-scoped runtime requests wit Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-session-manager-session-request", @@ -2462,10 +2455,10 @@ it.effect( Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-session-manager-request-expire", @@ -2543,10 +2536,10 @@ it.effect( Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-session-manager-multi-thread-active", @@ -2690,9 +2683,9 @@ it.effect( Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const now = yield* DateTime.now; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-session-manager-shared-runtime", @@ -2866,9 +2859,9 @@ it.effect( resumesBeforeUnload = (yield* Ref.get(state)).resumeCount; }); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const now = yield* DateTime.now; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-session-manager-unload-race", @@ -2961,9 +2954,9 @@ it.effect( Effect.gen(function* () { const state = yield* Ref.make(emptyState); const effect = Effect.gen(function* () { - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const manager = yield* ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; const now = yield* DateTime.now; const projectId = yield* idAllocator.allocate.project({ fixtureName: "provider-session-manager-exclusive-runtime", @@ -3023,10 +3016,10 @@ for (const workspaceState of ["missing", "file"] as const) { if (workspaceState === "file") yield* fileSystem.writeFileString(cwd, "not a directory"); const state = yield* Ref.make(emptyState); yield* Effect.gen(function* () { - const manager = yield* ProviderSessionManagerV2; - const eventSink = yield* EventSinkV2; - const projectionStore = yield* ProjectionStoreV2; - const idAllocator = yield* IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const threadId = ThreadId.make(`thread-${workspaceState}-workspace`); const providerSessionId = yield* idAllocator.allocate.providerSession({ providerInstanceId: modelSelection.instanceId, @@ -3073,10 +3066,10 @@ it.effect( yield* fileSystem.makeDirectory(cwd); const state = yield* Ref.make(emptyState); yield* Effect.gen(function* () { - const manager = yield* ProviderSessionManagerV2; - const eventSink = yield* EventSinkV2; - const projectionStore = yield* ProjectionStoreV2; - const idAllocator = yield* IdAllocatorV2; + const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; + const eventSink = yield* EventSink.EventSinkV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const threadId = ThreadId.make("thread-deleted-live-workspace"); const providerSessionId = yield* idAllocator.allocate.providerSession({ providerInstanceId: modelSelection.instanceId, diff --git a/apps/server/src/orchestration-v2/ProviderSessionManager.ts b/apps/server/src/orchestration-v2/ProviderSessionManager.ts index 5f831af79b83..786eb5a6bf0a 100644 --- a/apps/server/src/orchestration-v2/ProviderSessionManager.ts +++ b/apps/server/src/orchestration-v2/ProviderSessionManager.ts @@ -32,10 +32,10 @@ import * as ProjectService from "../project/ProjectService.ts"; import * as McpProviderSession from "../mcp/McpProviderSession.ts"; import * as ServerSettings from "../serverSettings.ts"; import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts"; -import { EventSinkV2 } from "./EventSink.ts"; -import { IdAllocatorV2 } from "./IdAllocator.ts"; +import * as EventSink from "./EventSink.ts"; +import * as IdAllocator from "./IdAllocator.ts"; import { makeKeyedSerialExecutor } from "./KeyedSerialExecutor.ts"; -import { ProviderEventIngestorV2 } from "./ProviderEventIngestor.ts"; +import * as ProviderEventIngestor from "./ProviderEventIngestor.ts"; import { ProviderAdapterEventStreamError, ProviderAdapterV2RuntimePolicy, @@ -44,8 +44,8 @@ import { type ProviderAdapterV2EventSubscription, type ProviderAdapterV2SessionRuntime, } from "./ProviderAdapter.ts"; -import { ProviderAdapterRegistryV2 } from "./ProviderAdapterRegistry.ts"; -import { ProjectionStoreV2 } from "./ProjectionStore.ts"; +import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; const DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000; const DEFAULT_MAX_IDLE_PIN_MS = 4 * 60 * 60 * 1000; @@ -293,18 +293,18 @@ export const layerWithOptions = ( ): Layer.Layer< ProviderSessionManagerV2, never, - | EventSinkV2 + | EventSink.EventSinkV2 | FileSystem.FileSystem - | IdAllocatorV2 + | IdAllocator.IdAllocatorV2 | McpSessionRegistry.McpSessionRegistry - | ProjectionStoreV2 - | ProviderEventIngestorV2 - | ProviderAdapterRegistryV2 + | ProjectionStore.ProjectionStoreV2 + | ProviderEventIngestor.ProviderEventIngestorV2 + | ProviderAdapterRegistry.ProviderAdapterRegistryV2 > => Layer.effect( ProviderSessionManagerV2, Effect.gen(function* () { - const registry = yield* ProviderAdapterRegistryV2; + const registry = yield* ProviderAdapterRegistry.ProviderAdapterRegistryV2; const fileSystem = yield* FileSystem.FileSystem; const mcpSessionRegistry = yield* McpSessionRegistry.McpSessionRegistry; /** @@ -317,10 +317,10 @@ export const layerWithOptions = ( */ const serverSettings = yield* Effect.serviceOption(ServerSettings.ServerSettingsService); const projectService = yield* Effect.serviceOption(ProjectService.ProjectService); - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const providerEventIngestor = yield* ProviderEventIngestorV2; - const projectionStore = yield* ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const providerEventIngestor = yield* ProviderEventIngestor.ProviderEventIngestorV2; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; const agentAccessSettings = Effect.fn("ProviderSessionManagerV2.agentAccessSettings")( function* (threadId: ThreadId) { if (Option.isNone(serverSettings)) return { browser: true, device: false }; diff --git a/apps/server/src/orchestration-v2/ProviderTurnControlService.test.ts b/apps/server/src/orchestration-v2/ProviderTurnControlService.test.ts index 2eec4cfcfa7b..eba830319aaf 100644 --- a/apps/server/src/orchestration-v2/ProviderTurnControlService.test.ts +++ b/apps/server/src/orchestration-v2/ProviderTurnControlService.test.ts @@ -23,13 +23,10 @@ import * as Ref from "effect/Ref"; import * as Stream from "effect/Stream"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; -import { ProjectionStoreV2 } from "./ProjectionStore.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; import type { ProviderAdapterV2SessionRuntime } from "./ProviderAdapter.ts"; -import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts"; -import { - layer as providerTurnControlLayer, - ProviderTurnControlServiceV2, -} from "./ProviderTurnControlService.ts"; +import * as ProviderSessionManager from "./ProviderSessionManager.ts"; +import * as ProviderTurnControlService from "./ProviderTurnControlService.ts"; const driver = ProviderDriverKind.make("codex"); const providerInstanceId = ProviderInstanceId.make("codex"); @@ -211,8 +208,8 @@ it.effect( forkThread: () => Effect.die("unused forkThread"), }; const projectionLayer = Layer.succeed( - ProjectionStoreV2, - ProjectionStoreV2.of({ + ProjectionStore.ProjectionStoreV2, + ProjectionStore.ProjectionStoreV2.of({ apply: () => Effect.void, getLimitRecoveryCandidates: () => Effect.die("unused getLimitRecoveryCandidates"), getShellSnapshot: () => Effect.die("unused getShellSnapshot"), @@ -261,8 +258,8 @@ it.effect( }), ); const sessionManagerLayer = Layer.succeed( - ProviderSessionManagerV2, - ProviderSessionManagerV2.of({ + ProviderSessionManager.ProviderSessionManagerV2, + ProviderSessionManager.ProviderSessionManagerV2.of({ shutdown: Effect.void, open: () => Effect.die("unused open"), get: (providerSessionId) => @@ -275,12 +272,12 @@ it.effect( detach: () => Effect.void, }), ); - const controlLayer = providerTurnControlLayer.pipe( + const controlLayer = ProviderTurnControlService.layer.pipe( Layer.provide(Layer.merge(projectionLayer, sessionManagerLayer)), ); const [ordinaryInterrupt, unrelatedRestart] = yield* Effect.gen(function* () { - const control = yield* ProviderTurnControlServiceV2; + const control = yield* ProviderTurnControlService.ProviderTurnControlServiceV2; const ordinary = yield* Effect.exit( control.interrupt({ threadId, @@ -307,7 +304,7 @@ it.effect( assert.isNull(yield* Ref.get(interruptedThread)); yield* Effect.gen(function* () { - const control = yield* ProviderTurnControlServiceV2; + const control = yield* ProviderTurnControlService.ProviderTurnControlServiceV2; yield* control.interruptAndAwaitTerminal({ threadId, providerSessionId: oldSessionId, diff --git a/apps/server/src/orchestration-v2/ProviderTurnControlService.ts b/apps/server/src/orchestration-v2/ProviderTurnControlService.ts index 2506d2830a06..3a80e2af5d85 100644 --- a/apps/server/src/orchestration-v2/ProviderTurnControlService.ts +++ b/apps/server/src/orchestration-v2/ProviderTurnControlService.ts @@ -13,8 +13,8 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; -import { ProjectionStoreV2 } from "./ProjectionStore.ts"; -import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; +import * as ProviderSessionManager from "./ProviderSessionManager.ts"; const yieldToRuntime = Effect.yieldNow.pipe( Effect.andThen( @@ -72,12 +72,12 @@ export class ProviderTurnControlServiceV2 extends Context.Service< export const layer: Layer.Layer< ProviderTurnControlServiceV2, never, - ProjectionStoreV2 | ProviderSessionManagerV2 + ProjectionStore.ProjectionStoreV2 | ProviderSessionManager.ProviderSessionManagerV2 > = Layer.effect( ProviderTurnControlServiceV2, Effect.gen(function* () { - const projections = yield* ProjectionStoreV2; - const sessions = yield* ProviderSessionManagerV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; + const sessions = yield* ProviderSessionManager.ProviderSessionManagerV2; const load = (input: { readonly threadId: ThreadId; diff --git a/apps/server/src/orchestration-v2/ProviderTurnStartService.test.ts b/apps/server/src/orchestration-v2/ProviderTurnStartService.test.ts index b79e983e1311..746fbe235d99 100644 --- a/apps/server/src/orchestration-v2/ProviderTurnStartService.test.ts +++ b/apps/server/src/orchestration-v2/ProviderTurnStartService.test.ts @@ -26,7 +26,7 @@ import * as Schema from "effect/Schema"; import * as GitWorkflow from "../git/GitWorkflowService.ts"; import * as ProjectService from "../project/ProjectService.ts"; -import { ProviderAuthService } from "../provider/Services/ProviderAuthService.ts"; +import * as ProviderAuthService from "../provider/Services/ProviderAuthService.ts"; import * as ContextHandoffService from "./ContextHandoffService.ts"; import * as EventSink from "./EventSink.ts"; import * as IdAllocator from "./IdAllocator.ts"; @@ -125,7 +125,9 @@ it("does not commit running state when inherited background routing cannot be re }, }), Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({}), - Layer.mock(ProviderAuthService)({ tryHandlePromptCommand: () => Effect.succeed(false) }), + Layer.mock(ProviderAuthService.ProviderAuthService)({ + tryHandlePromptCommand: () => Effect.succeed(false), + }), Layer.mock(RunExecutionService.RunExecutionServiceV2)({ startRootRun }), Layer.mock(RuntimePolicy.RuntimePolicyV2)({}), ), @@ -486,7 +488,7 @@ function makeLocalCommandHarness(input: { ), }), Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ open }), - Layer.mock(ProviderAuthService)({ tryHandlePromptCommand }), + Layer.mock(ProviderAuthService.ProviderAuthService)({ tryHandlePromptCommand }), Layer.mock(RunExecutionService.RunExecutionServiceV2)({ startRootRun }), Layer.mock(RuntimePolicy.RuntimePolicyV2)({ resolve: () => Effect.succeed({} as never), diff --git a/apps/server/src/orchestration-v2/ProviderTurnStartService.ts b/apps/server/src/orchestration-v2/ProviderTurnStartService.ts index cfe847e4ccc3..4ea41f4ca9b8 100644 --- a/apps/server/src/orchestration-v2/ProviderTurnStartService.ts +++ b/apps/server/src/orchestration-v2/ProviderTurnStartService.ts @@ -20,10 +20,10 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; -import { GitWorkflowService } from "../git/GitWorkflowService.ts"; -import { ProjectService } from "../project/ProjectService.ts"; -import { ProviderAuthService } from "../provider/Services/ProviderAuthService.ts"; -import { EventSinkV2 } from "./EventSink.ts"; +import * as GitWorkflowService from "../git/GitWorkflowService.ts"; +import * as ProjectService from "../project/ProjectService.ts"; +import * as ProviderAuthService from "../provider/Services/ProviderAuthService.ts"; +import * as EventSink from "./EventSink.ts"; import * as ContextHandoffService from "./ContextHandoffService.ts"; import { DEFAULT_HANDOFF_TOKEN_CAP, @@ -41,16 +41,12 @@ import { type ProviderAdapterV2HistoricalContext, type ProviderAdapterV2SessionRuntime, } from "./ProviderAdapter.ts"; -import { IdAllocatorV2 } from "./IdAllocator.ts"; -import { ProjectionStoreV2, type ProjectionRuntimeRecoveryState } from "./ProjectionStore.ts"; -import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts"; +import * as IdAllocator from "./IdAllocator.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; +import * as ProviderSessionManager from "./ProviderSessionManager.ts"; import { makeProviderFailure } from "./ProviderFailure.ts"; -import { - canRouteRelatedSubagent, - RunExecutionServiceV2, - selectInheritedBackgroundTurnItems, -} from "./RunExecutionService.ts"; -import { RuntimePolicyV2 } from "./RuntimePolicy.ts"; +import * as RunExecutionService from "./RunExecutionService.ts"; +import * as RuntimePolicy from "./RuntimePolicy.ts"; import { isRestartNoteContinuation, pendingRestartCancelledBackgroundWork, @@ -88,31 +84,31 @@ export class ProviderTurnStartServiceV2 extends Context.Service< export const layer: Layer.Layer< ProviderTurnStartServiceV2, never, - | EventSinkV2 + | EventSink.EventSinkV2 | ContextHandoffService.ContextHandoffServiceV2 - | IdAllocatorV2 + | IdAllocator.IdAllocatorV2 | FileSystem.FileSystem - | GitWorkflowService - | ProjectService - | ProviderAuthService - | ProjectionStoreV2 - | ProviderSessionManagerV2 - | RunExecutionServiceV2 - | RuntimePolicyV2 + | GitWorkflowService.GitWorkflowService + | ProjectService.ProjectService + | ProviderAuthService.ProviderAuthService + | ProjectionStore.ProjectionStoreV2 + | ProviderSessionManager.ProviderSessionManagerV2 + | RunExecutionService.RunExecutionServiceV2 + | RuntimePolicy.RuntimePolicyV2 > = Layer.effect( ProviderTurnStartServiceV2, Effect.gen(function* () { - const eventSink = yield* EventSinkV2; + const eventSink = yield* EventSink.EventSinkV2; const contextHandoffService = yield* ContextHandoffService.ContextHandoffServiceV2; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const fileSystem = yield* FileSystem.FileSystem; - const gitWorkflow = yield* GitWorkflowService; - const projects = yield* ProjectService; - const providerAuth = yield* ProviderAuthService; - const projectionStore = yield* ProjectionStoreV2; - const providerSessions = yield* ProviderSessionManagerV2; - const runExecution = yield* RunExecutionServiceV2; - const runtimePolicy = yield* RuntimePolicyV2; + const gitWorkflow = yield* GitWorkflowService.GitWorkflowService; + const projects = yield* ProjectService.ProjectService; + const providerAuth = yield* ProviderAuthService.ProviderAuthService; + const projectionStore = yield* ProjectionStore.ProjectionStoreV2; + const providerSessions = yield* ProviderSessionManager.ProviderSessionManagerV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; + const runtimePolicy = yield* RuntimePolicy.RuntimePolicyV2; // These callbacks outlive startup while a run drains background work. Build // them outside start's scope so they cannot retain its full thread history. @@ -122,7 +118,9 @@ export const layer: Layer.Layer< readonly attemptId: OrchestrationV2RunAttempt["id"]; readonly providerThreadId: OrchestrationV2ProviderThread["id"]; readonly runOrdinal: number; - readonly inheritedBackgroundTurnItems: ReturnType; + readonly inheritedBackgroundTurnItems: ReturnType< + typeof RunExecutionService.selectInheritedBackgroundTurnItems + >; }) => { // Guards and background routing need live execution state, not a fresh // allocation of every completed message and tool output in the thread. @@ -139,7 +137,7 @@ export const layer: Layer.Layer< loadInheritedBackgroundTurnItems: () => projectionStore.getRuntimeRecoveryProjection(input.threadId).pipe( Effect.map((current) => - selectInheritedBackgroundTurnItems({ + RunExecutionService.selectInheritedBackgroundTurnItems({ threadId: input.threadId, currentProviderThreadId: input.providerThreadId, currentRunOrdinal: input.runOrdinal, @@ -494,9 +492,9 @@ export const layer: Layer.Layer< } } const selectInheritedBackgroundItems = ( - current: ProjectionRuntimeRecoveryState, - ): ReturnType => - selectInheritedBackgroundTurnItems({ + current: ProjectionStore.ProjectionRuntimeRecoveryState, + ): ReturnType => + RunExecutionService.selectInheritedBackgroundTurnItems({ threadId: current.thread.id, currentProviderThreadId: providerThread.id, currentRunOrdinal: run.ordinal, @@ -940,7 +938,7 @@ export const layer: Layer.Layer< return; } const routableSubagents = projection.subagents.filter((subagent) => - canRouteRelatedSubagent(subagent.status), + RunExecutionService.canRouteRelatedSubagent(subagent.status), ); const userText = projectComposerContextForProvider({ text: message.text, diff --git a/apps/server/src/orchestration-v2/PullRequestSyncReactor.test.ts b/apps/server/src/orchestration-v2/PullRequestSyncReactor.test.ts index 79b2cbc6067f..524afd9d8646 100644 --- a/apps/server/src/orchestration-v2/PullRequestSyncReactor.test.ts +++ b/apps/server/src/orchestration-v2/PullRequestSyncReactor.test.ts @@ -22,10 +22,10 @@ import * as Queue from "effect/Queue"; import * as Ref from "effect/Ref"; import { TestClock } from "effect/testing"; -import { PullRequestService } from "../pullRequest/PullRequestService.ts"; -import { ServerActivation } from "../serverActivation.ts"; -import { OrchestratorV2, type OrchestratorV2Shape } from "./Orchestrator.ts"; -import { ProjectionStoreV2 } from "./ProjectionStore.ts"; +import * as PullRequestService from "../pullRequest/PullRequestService.ts"; +import * as ServerActivation from "../serverActivation.ts"; +import * as Orchestrator from "./Orchestrator.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; import * as PullRequestSyncReactor from "./PullRequestSyncReactor.ts"; import type { PullRequestTestThread } from "./testkit/pullRequestFixtures.ts"; @@ -152,7 +152,7 @@ function makeSummary( } interface HarnessOptions { - readonly invalidate?: PullRequestService["Service"]["invalidate"]; + readonly invalidate?: PullRequestService.PullRequestService["Service"]["invalidate"]; readonly snapshot: TestShellSnapshot; readonly summary?: ( input: PullRequestRef, @@ -172,21 +172,24 @@ const makeHarness = Effect.fn("makePullRequestSyncHarness")(function* (options: const summaryCalls = yield* Ref.make>([]); const stackCalls = yield* Ref.make>([]); - const summary: PullRequestService["Service"]["summary"] = (input, readOptions) => + const summary: PullRequestService.PullRequestService["Service"]["summary"] = ( + input, + readOptions, + ) => Effect.gen(function* () { assert.strictEqual(readOptions?.recoverTransientFailure, false); yield* Ref.update(summaryCalls, (calls) => [...calls, input]); return yield* options.summary?.(input) ?? Effect.succeed(makeSummary(input)); }); - const stack: PullRequestService["Service"]["stack"] = (input, readOptions) => + const stack: PullRequestService.PullRequestService["Service"]["stack"] = (input, readOptions) => Effect.gen(function* () { assert.strictEqual(readOptions?.includeDetails, false); yield* Ref.update(stackCalls, (calls) => [...calls, input]); return yield* options.stack?.(input) ?? Effect.succeed(null); }); - const dispatch: OrchestratorV2Shape["dispatch"] = (command) => { + const dispatch: Orchestrator.OrchestratorV2Shape["dispatch"] = (command) => { if (command.type === "thread.pull-request-link.sync") { return Ref.update(syncCommands, (recorded) => [...recorded, command]).pipe( Effect.as({ sequence: 1, storedEvents: [] }), @@ -201,12 +204,12 @@ const makeHarness = Effect.fn("makePullRequestSyncHarness")(function* (options: }; const dependencies = Layer.mergeAll( - Layer.mock(PullRequestService)({ + Layer.mock(PullRequestService.PullRequestService)({ summary, stack, invalidate: options.invalidate ?? (() => Effect.void), }), - Layer.mock(ProjectionStoreV2)({ + Layer.mock(ProjectionStore.ProjectionStoreV2)({ // Mirrors the store's filter: active threads that have at least one link. getThreadsWithPullRequests: () => Queue.offer(snapshotReads, undefined).pipe( @@ -224,7 +227,7 @@ const makeHarness = Effect.fn("makePullRequestSyncHarness")(function* (options: ), ), }), - Layer.mock(OrchestratorV2)({ + Layer.mock(Orchestrator.OrchestratorV2)({ getShellSnapshot: () => Ref.update(shellSnapshotReads, (count) => count + 1).pipe( Effect.andThen(Effect.die(new Error("pull request sync must not read the shell"))), @@ -232,7 +235,7 @@ const makeHarness = Effect.fn("makePullRequestSyncHarness")(function* (options: dispatch, streamDomainEvents: Stream.empty, }), - Layer.succeed(ServerActivation, Deferred.await(activation)), + Layer.succeed(ServerActivation.ServerActivation, Deferred.await(activation)), Layer.succeed(Crypto.Crypto, testCrypto), ); diff --git a/apps/server/src/orchestration-v2/PullRequestSyncReactor.ts b/apps/server/src/orchestration-v2/PullRequestSyncReactor.ts index dffb2ab67912..af6b5c2972ba 100644 --- a/apps/server/src/orchestration-v2/PullRequestSyncReactor.ts +++ b/apps/server/src/orchestration-v2/PullRequestSyncReactor.ts @@ -27,15 +27,15 @@ import * as Stream from "effect/Stream"; import * as PullRequestService from "../pullRequest/PullRequestService.ts"; import { forkParked } from "../serverActivation.ts"; -import { OrchestratorV2 } from "./Orchestrator.ts"; -import { ProjectionStoreV2, type ProjectionThreadPullRequests } from "./ProjectionStore.ts"; +import * as Orchestrator from "./Orchestrator.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; const SLOW_SYNC_INTERVAL_MS = 15 * 60 * 1_000; type SnapshotFields = Omit; interface LinkEntry { - readonly thread: ProjectionThreadPullRequests; + readonly thread: ProjectionStore.ProjectionThreadPullRequests; readonly link: ThreadPullRequestLink; } @@ -103,7 +103,7 @@ function stacksEqual( ); } -function isUnsettled(thread: ProjectionThreadPullRequests): boolean { +function isUnsettled(thread: ProjectionStore.ProjectionThreadPullRequests): boolean { return thread.settledOverride !== "settled" && thread.settledAt === null; } @@ -125,8 +125,8 @@ export class PullRequestSyncReactor extends Context.Service< /** @public Service construction is part of the canonical Effect module API. */ export const make = Effect.gen(function* () { - const engine = yield* OrchestratorV2; - const projections = yield* ProjectionStoreV2; + const engine = yield* Orchestrator.OrchestratorV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const pullRequests = yield* PullRequestService.PullRequestService; const crypto = yield* Crypto.Crypto; diff --git a/apps/server/src/orchestration-v2/RestartContinuation.test.ts b/apps/server/src/orchestration-v2/RestartContinuation.test.ts index 493797a08ee4..f43f4cd01666 100644 --- a/apps/server/src/orchestration-v2/RestartContinuation.test.ts +++ b/apps/server/src/orchestration-v2/RestartContinuation.test.ts @@ -15,7 +15,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as ServerSettings from "../serverSettings.ts"; import { restartContinuationRun, continueRestartedRun } from "./RestartContinuation.ts"; -import { ThreadManagementService } from "./ThreadManagementService.ts"; +import * as ThreadManagementService from "./ThreadManagementService.ts"; import * as ProviderRuntimeRecovery from "./ProviderRuntimeRecoveryService.ts"; import * as ProjectionStore from "./ProjectionStore.ts"; import * as EventSink from "./EventSink.ts"; @@ -193,11 +193,13 @@ it.effect("prompts a settled thread's continuation with the note of its lost wor runs: [{ ...base.runs[0]!, status: "completed", restartCancelledBackgroundWork: work }], providerTurns: [{ ...base.providerTurns[0]!, status: "completed" }], } as unknown as OrchestrationV2ThreadProjection; - const commands: Parameters[0][] = []; + const commands: Parameters< + ThreadManagementService.ThreadManagementService["Service"]["dispatch"] + >[0][] = []; yield* continueRestartedRun({ threadId, sourceRunId: runId }).pipe( Effect.provide( Layer.merge( - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: () => Effect.succeed(projection), dispatch: (command) => { commands.push(command); @@ -236,11 +238,13 @@ it.effect("does not continue a failed run that lost background work", () => ], providerTurns: [{ ...base.providerTurns[0]!, status: "failed" }], } as unknown as OrchestrationV2ThreadProjection; - const commands: Parameters[0][] = []; + const commands: Parameters< + ThreadManagementService.ThreadManagementService["Service"]["dispatch"] + >[0][] = []; yield* continueRestartedRun({ threadId, sourceRunId: runId }).pipe( Effect.provide( Layer.merge( - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: () => Effect.succeed(projection), dispatch: (command) => { commands.push(command); @@ -321,8 +325,10 @@ it.effect("does not duplicate delivery and yields to newer user work or opt-out" Effect.gen(function* () { let projection = makeProjection(); projection = { ...projection, runs: [{ ...projection.runs[0]!, status: "cancelled" }] }; - const commands: Parameters[0][] = []; - const threads = Layer.mock(ThreadManagementService)({ + const commands: Parameters< + ThreadManagementService.ThreadManagementService["Service"]["dispatch"] + >[0][] = []; + const threads = Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: () => Effect.succeed(projection), dispatch: (command) => { commands.push(command); @@ -493,7 +499,7 @@ it.effect("does not cancel or resume a run that completes while shutdown intent Effect.provide( Layer.mergeAll( ServerSettings.layerTest({ continueThreadsAfterServerUpdate: true }), - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ getThreadRecords: () => Effect.succeed(projection), dispatch: () => Effect.sync(() => { diff --git a/apps/server/src/orchestration-v2/RestartContinuation.ts b/apps/server/src/orchestration-v2/RestartContinuation.ts index b577de118387..216b830de41e 100644 --- a/apps/server/src/orchestration-v2/RestartContinuation.ts +++ b/apps/server/src/orchestration-v2/RestartContinuation.ts @@ -10,8 +10,8 @@ import { import * as Effect from "effect/Effect"; import type { ProjectionRuntimeRecoveryState } from "./ProjectionStore.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; -import { ThreadManagementService } from "./ThreadManagementService.ts"; +import * as ServerSettings from "../serverSettings.ts"; +import * as ThreadManagementService from "./ThreadManagementService.ts"; import { isRestartNoteSource, restartCancelledBackgroundWorkNote, @@ -91,10 +91,10 @@ export function restartContinuationRun( export const continueRestartedRun = Effect.fn("RestartContinuation.continueRestartedRun")( function* (input: { readonly threadId: ThreadId; readonly sourceRunId: RunId }) { - const settings = yield* ServerSettingsService; + const settings = yield* ServerSettings.ServerSettingsService; const enabled = yield* settings.getSettings.pipe(Effect.orElseSucceed(() => null)); if (!enabled) return; - const threads = yield* ThreadManagementService; + const threads = yield* ThreadManagementService.ThreadManagementService; const messageId = MessageId.make(`message:restart-continuation:${input.sourceRunId}`); const projection = yield* threads.getThreadRecords( input.threadId, diff --git a/apps/server/src/orchestration-v2/RunCompletionReads.test.ts b/apps/server/src/orchestration-v2/RunCompletionReads.test.ts index 8746ce904331..89d5cd3e1291 100644 --- a/apps/server/src/orchestration-v2/RunCompletionReads.test.ts +++ b/apps/server/src/orchestration-v2/RunCompletionReads.test.ts @@ -14,23 +14,18 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; -import { - ProjectionStoreV2, - ProjectionStoreThreadNotFoundError, - layer, - layerMemory, -} from "./ProjectionStore.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; const databaseLayer = Layer.mergeAll( SqlitePersistenceMemory, - layer.pipe(Layer.provide(SqlitePersistenceMemory)), + ProjectionStore.layer.pipe(Layer.provide(SqlitePersistenceMemory)), ); it.effect.each(["sqlite", "memory"] as const)( "checks queue eligibility and delivery ownership without unrelated history in %s", (storage) => Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:completion-reads"); const runId = RunId.make("run:completion-reads"); @@ -151,15 +146,17 @@ it.effect.each(["sqlite", "memory"] as const)( const missing = ThreadId.make("missing-thread"); assert.instanceOf( yield* store.canStartQueuedRun(missing).pipe(Effect.flip), - ProjectionStoreThreadNotFoundError, + ProjectionStore.ProjectionStoreThreadNotFoundError, ); assert.instanceOf( yield* store.getRunMessage(missing, runId).pipe(Effect.flip), - ProjectionStoreThreadNotFoundError, + ProjectionStore.ProjectionStoreThreadNotFoundError, ); }).pipe( Effect.provide( - storage === "sqlite" ? databaseLayer : Layer.merge(SqlitePersistenceMemory, layerMemory), + storage === "sqlite" + ? databaseLayer + : Layer.merge(SqlitePersistenceMemory, ProjectionStore.layerMemory), ), ), ); diff --git a/apps/server/src/orchestration-v2/RunExecutionService.test.ts b/apps/server/src/orchestration-v2/RunExecutionService.test.ts index 0aa6c5b9917c..633127e5c7df 100644 --- a/apps/server/src/orchestration-v2/RunExecutionService.test.ts +++ b/apps/server/src/orchestration-v2/RunExecutionService.test.ts @@ -38,11 +38,11 @@ import * as Ref from "effect/Ref"; import * as Stream from "effect/Stream"; import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; -import { CheckpointBaselineCaptureError, CheckpointServiceV2 } from "./CheckpointService.ts"; +import * as ServerSettings from "../serverSettings.ts"; +import * as CheckpointService from "./CheckpointService.ts"; import type { PendingOrchestrationEffectV2 } from "./EffectOutbox.ts"; -import { EventSinkV2 } from "./EventSink.ts"; -import { IdAllocatorV2, layer as idAllocatorLayer } from "./IdAllocator.ts"; +import * as EventSink from "./EventSink.ts"; +import * as IdAllocator from "./IdAllocator.ts"; import { ProviderAdapterEventStreamError, ProviderAdapterTurnStartError, @@ -50,49 +50,41 @@ import { type ProviderAdapterV2Event, type ProviderAdapterV2SessionRuntime, } from "./ProviderAdapter.ts"; -import { ProviderEventIngestorV2 } from "./ProviderEventIngestor.ts"; -import { - canRouteRelatedSubagent, - cascadeTerminalizeRunOwnedSubagents, - finalProviderThreadStatus, - layer as runExecutionServiceLayer, - makeProviderEventRoutingState, - type ProviderEventRouteIdentity, - routeProviderEvent, - RunExecutionServiceV2, - selectInheritedBackgroundTurnItems, -} from "./RunExecutionService.ts"; -import { RunFinalizationObserver } from "./RunFinalizationService.ts"; +import * as ProviderEventIngestor from "./ProviderEventIngestor.ts"; +import * as RunExecutionService from "./RunExecutionService.ts"; +import * as RunFinalizationService from "./RunFinalizationService.ts"; const driver = ProviderDriverKind.make("codex"); -const RunExecutionTestLayer = runExecutionServiceLayer.pipe( +const RunExecutionTestLayer = RunExecutionService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ captureBaseline: () => Effect.void }), - Layer.mock(EventSinkV2)({}), - idAllocatorLayer, - Layer.mock(ProviderEventIngestorV2)({ ingestNormalized: () => Effect.succeed([]) }), - ServerSettingsService.layerTest(), + Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), + Layer.mock(EventSink.EventSinkV2)({}), + IdAllocator.layer, + Layer.mock(ProviderEventIngestor.ProviderEventIngestorV2)({ + ingestNormalized: () => Effect.succeed([]), + }), + ServerSettings.layerTest(), ), ), ); it("keeps recoverable turn failures reusable and reserves error for broken threads", () => { - assert.equal(finalProviderThreadStatus("reusable"), "idle"); - assert.equal(finalProviderThreadStatus("broken"), "error"); + assert.equal(RunExecutionService.finalProviderThreadStatus("reusable"), "idle"); + assert.equal(RunExecutionService.finalProviderThreadStatus("broken"), "error"); }); it.effect("routes shared-runtime events only to their owning root run", () => Effect.gen(function* () { const now = yield* DateTime.now; - const first: ProviderEventRouteIdentity = { + const first: RunExecutionService.ProviderEventRouteIdentity = { threadId: ThreadId.make("thread:shared-runtime:first"), runId: RunId.make("run:shared-runtime:first"), attemptId: RunAttemptId.make("attempt:shared-runtime:first"), providerThreadId: ProviderThreadId.make("provider-thread:shared-runtime:first"), }; - const second: ProviderEventRouteIdentity = { + const second: RunExecutionService.ProviderEventRouteIdentity = { threadId: ThreadId.make("thread:shared-runtime:second"), runId: RunId.make("run:shared-runtime:second"), attemptId: RunAttemptId.make("attempt:shared-runtime:second"), @@ -144,16 +136,20 @@ it.effect("routes shared-runtime events only to their owning root run", () => threadDisposition: "reusable", }; - const firstInitial = makeProviderEventRoutingState({ + const firstInitial = RunExecutionService.makeProviderEventRoutingState({ identity: first, providerTurnId: null, }); - const secondInitial = makeProviderEventRoutingState({ + const secondInitial = RunExecutionService.makeProviderEventRoutingState({ identity: second, providerTurnId: null, }); - const [firstTurnAccepted, firstAfterTurn] = routeProviderEvent(turnEvent, first, firstInitial); - const [secondTurnAccepted, secondAfterTurn] = routeProviderEvent( + const [firstTurnAccepted, firstAfterTurn] = RunExecutionService.routeProviderEvent( + turnEvent, + first, + firstInitial, + ); + const [secondTurnAccepted, secondAfterTurn] = RunExecutionService.routeProviderEvent( turnEvent, second, secondInitial, @@ -161,23 +157,27 @@ it.effect("routes shared-runtime events only to their owning root run", () => assert.isTrue(firstTurnAccepted); assert.isFalse(secondTurnAccepted); - assert.isTrue(routeProviderEvent(messageEvent, first, firstAfterTurn)[0]); - assert.isFalse(routeProviderEvent(messageEvent, second, secondAfterTurn)[0]); - assert.isTrue(routeProviderEvent(terminalEvent, first, firstAfterTurn)[0]); - assert.isFalse(routeProviderEvent(terminalEvent, second, secondAfterTurn)[0]); + assert.isTrue(RunExecutionService.routeProviderEvent(messageEvent, first, firstAfterTurn)[0]); + assert.isFalse( + RunExecutionService.routeProviderEvent(messageEvent, second, secondAfterTurn)[0], + ); + assert.isTrue(RunExecutionService.routeProviderEvent(terminalEvent, first, firstAfterTurn)[0]); + assert.isFalse( + RunExecutionService.routeProviderEvent(terminalEvent, second, secondAfterTurn)[0], + ); }), ); it("does not route a superseded attempt through a reused provider thread", () => { const threadId = ThreadId.make("thread:shared-runtime:restart"); const providerThreadId = ProviderThreadId.make("provider-thread:shared-runtime:restart"); - const oldAttempt: ProviderEventRouteIdentity = { + const oldAttempt: RunExecutionService.ProviderEventRouteIdentity = { threadId, runId: RunId.make("run:shared-runtime:restart"), attemptId: RunAttemptId.make("attempt:shared-runtime:restart:old"), providerThreadId, }; - const newAttempt: ProviderEventRouteIdentity = { + const newAttempt: RunExecutionService.ProviderEventRouteIdentity = { ...oldAttempt, attemptId: RunAttemptId.make("attempt:shared-runtime:restart:new"), }; @@ -198,8 +198,11 @@ it("does not route a superseded attempt through a reused provider thread", () => }, }; - const newState = makeProviderEventRoutingState({ identity: newAttempt, providerTurnId: null }); - assert.isFalse(routeProviderEvent(oldTurnEvent, newAttempt, newState)[0]); + const newState = RunExecutionService.makeProviderEventRoutingState({ + identity: newAttempt, + providerTurnId: null, + }); + assert.isFalse(RunExecutionService.routeProviderEvent(oldTurnEvent, newAttempt, newState)[0]); }); it("routes only exact same-thread background items inherited from settled runs", () => { @@ -208,13 +211,13 @@ it("routes only exact same-thread background items inherited from settled runs", const priorRunId = RunId.make("run:inherited-background-routing:prior"); const currentRunId = RunId.make("run:inherited-background-routing:current"); const itemId = TurnItemId.make("turn-item:inherited-background-routing"); - const identity: ProviderEventRouteIdentity = { + const identity: RunExecutionService.ProviderEventRouteIdentity = { threadId, runId: currentRunId, attemptId: RunAttemptId.make("attempt:inherited-background-routing:current"), providerThreadId: ProviderThreadId.make("provider-thread:inherited-background-routing:current"), }; - const initial = makeProviderEventRoutingState({ + const initial = RunExecutionService.makeProviderEventRoutingState({ identity, inheritedBackgroundTurnItems: [{ id: itemId, runId: priorRunId }], providerTurnId: null, @@ -259,21 +262,31 @@ it("routes only exact same-thread background items inherited from settled runs", turnItem: { ...inheritedRunning.turnItem, status: "completed" as const }, } as ProviderAdapterV2Event; - const [runningAccepted, afterRunning] = routeProviderEvent(inheritedRunning, identity, initial); + const [runningAccepted, afterRunning] = RunExecutionService.routeProviderEvent( + inheritedRunning, + identity, + initial, + ); assert.isTrue(runningAccepted); - assert.isFalse(routeProviderEvent(unrelatedRunItem, identity, afterRunning)[0]); - assert.isFalse(routeProviderEvent(unlistedPriorItem, identity, afterRunning)[0]); - assert.isFalse(routeProviderEvent(unrelatedThreadItem, identity, afterRunning)[0]); - assert.isFalse(routeProviderEvent(ordinaryItem, identity, afterRunning)[0]); + assert.isFalse( + RunExecutionService.routeProviderEvent(unrelatedRunItem, identity, afterRunning)[0], + ); + assert.isFalse( + RunExecutionService.routeProviderEvent(unlistedPriorItem, identity, afterRunning)[0], + ); + assert.isFalse( + RunExecutionService.routeProviderEvent(unrelatedThreadItem, identity, afterRunning)[0], + ); + assert.isFalse(RunExecutionService.routeProviderEvent(ordinaryItem, identity, afterRunning)[0]); - const [terminalAccepted, afterTerminal] = routeProviderEvent( + const [terminalAccepted, afterTerminal] = RunExecutionService.routeProviderEvent( inheritedTerminal, identity, afterRunning, ); assert.isTrue(terminalAccepted); assert.isFalse( - routeProviderEvent(inheritedRunning, identity, afterTerminal)[0], + RunExecutionService.routeProviderEvent(inheritedRunning, identity, afterTerminal)[0], "a nonterminal replay must not resurrect an inherited terminal", ); }); @@ -325,7 +338,7 @@ it("selects only live background items from non-completed settled prior runs", ( status, }) as OrchestrationV2TurnItem; - const selected = selectInheritedBackgroundTurnItems({ + const selected = RunExecutionService.selectInheritedBackgroundTurnItems({ threadId, currentProviderThreadId, currentRunOrdinal: 6, @@ -394,24 +407,26 @@ it("selects only live background items from non-completed settled prior runs", ( }); it("does not carry interrupted child ownership into later attempts", () => { - assert.isFalse(canRouteRelatedSubagent("interrupted")); - assert.isFalse(canRouteRelatedSubagent("failed")); - assert.isFalse(canRouteRelatedSubagent("cancelled")); - assert.isTrue(canRouteRelatedSubagent("completed")); - assert.isTrue(canRouteRelatedSubagent("running")); + assert.isFalse(RunExecutionService.canRouteRelatedSubagent("interrupted")); + assert.isFalse(RunExecutionService.canRouteRelatedSubagent("failed")); + assert.isFalse(RunExecutionService.canRouteRelatedSubagent("cancelled")); + assert.isTrue(RunExecutionService.canRouteRelatedSubagent("completed")); + assert.isTrue(RunExecutionService.canRouteRelatedSubagent("running")); const threadId = ThreadId.make("thread:related-child:next-attempt"); const childThreadId = ThreadId.make("thread:related-child:interrupted"); - const identity: ProviderEventRouteIdentity = { + const identity: RunExecutionService.ProviderEventRouteIdentity = { threadId, runId: RunId.make("run:related-child:next-attempt"), attemptId: RunAttemptId.make("attempt:related-child:next-attempt"), providerThreadId: ProviderThreadId.make("provider-thread:related-child:next-attempt"), }; - const state = makeProviderEventRoutingState({ + const state = RunExecutionService.makeProviderEventRoutingState({ identity, providerTurnId: null, - relatedThreadIds: canRouteRelatedSubagent("interrupted") ? [childThreadId] : [], + relatedThreadIds: RunExecutionService.canRouteRelatedSubagent("interrupted") + ? [childThreadId] + : [], }); const childNodeId = NodeId.make("node:related-child:interrupted"); const lateChildNode = { @@ -436,12 +451,12 @@ it("does not carry interrupted child ownership into later attempts", () => { }, } satisfies ProviderAdapterV2Event; - assert.isFalse(routeProviderEvent(lateChildNode, identity, state)[0]); + assert.isFalse(RunExecutionService.routeProviderEvent(lateChildNode, identity, state)[0]); }); it.effect("rechecks run ownership immediately before calling the provider", () => Effect.gen(function* () { - const runExecution = yield* RunExecutionServiceV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; const guardCalls = yield* Ref.make(0); const providerStarts = yield* Ref.make(0); const threadId = ThreadId.make("thread:run-execution-start-guard"); @@ -517,7 +532,7 @@ it.effect( "dispatches only attachment-free compact commands through the native compaction path", () => Effect.gen(function* () { - const runExecution = yield* RunExecutionServiceV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; const calls: Array = []; const attachment = ChatFileAttachment.make({ type: "file", @@ -598,7 +613,7 @@ it.effect( it.effect("refreshes MCP credential liveness before calling the provider", () => Effect.gen(function* () { - const runExecution = yield* RunExecutionServiceV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; const order = yield* Ref.make>([]); const threadId = ThreadId.make("thread:run-execution-mcp-liveness"); const touchActiveMcpThread = vi @@ -688,35 +703,37 @@ it.effect("starts the provider when checkpoint baseline capture fails", () => effects: ReadonlyArray; }> >([]); - const testLayer = runExecutionServiceLayer.pipe( + const testLayer = RunExecutionService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.fail( - new CheckpointBaselineCaptureError({ + new CheckpointService.CheckpointBaselineCaptureError({ scopeId: checkpointScope.id, ordinalWithinScope: 0, cause: new Error("VCS process timed out"), }), ), }), - Layer.mock(EventSinkV2)({ + Layer.mock(EventSink.EventSinkV2)({ writeWithEffects: (input) => Ref.update(writes, (current) => [ ...current, { events: input.events, effects: input.effects }, ]).pipe(Effect.as([])), }), - idAllocatorLayer, - Layer.mock(ProviderEventIngestorV2)({ ingestNormalized: () => Effect.succeed([]) }), - ServerSettingsService.layerTest(), + IdAllocator.layer, + Layer.mock(ProviderEventIngestor.ProviderEventIngestorV2)({ + ingestNormalized: () => Effect.succeed([]), + }), + ServerSettings.layerTest(), ), ), ); yield* Effect.gen(function* () { - const runExecution = yield* RunExecutionServiceV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; yield* runExecution.startRootRun({ commandId: CommandId.make("command:run-execution-baseline-failure"), appThread: { id: threadId } as OrchestrationV2AppThread, @@ -802,14 +819,14 @@ for (const scenario of ["failure", "interruption", "stale-attempt", "start-guard const refreshes = yield* Ref.make(0); const guardedWrites = yield* Ref.make(0); const writes = yield* Ref.make>>([]); - const testLayer = runExecutionServiceLayer.pipe( + const testLayer = RunExecutionService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => scenario === "start-guard" ? Effect.void : Effect.die("not reached"), }), - Layer.mock(EventSinkV2)({ + Layer.mock(EventSink.EventSinkV2)({ writeIfRunCurrent: (input) => Effect.gen(function* () { assert.equal(input.threadId, threadId); @@ -824,11 +841,13 @@ for (const scenario of ["failure", "interruption", "stale-attempt", "start-guard return { committed: true, storedEvents: [] }; }), }), - idAllocatorLayer, - Layer.mock(ProviderEventIngestorV2)({ ingestNormalized: () => Effect.succeed([]) }), + IdAllocator.layer, + Layer.mock(ProviderEventIngestor.ProviderEventIngestorV2)({ + ingestNormalized: () => Effect.succeed([]), + }), scenario === "start-guard" - ? ServerSettingsService.layerTest() - : Layer.mock(ServerSettingsService)({ + ? ServerSettings.layerTest() + : Layer.mock(ServerSettings.ServerSettingsService)({ getSettings: scenario === "interruption" ? Effect.interrupt @@ -840,7 +859,7 @@ for (const scenario of ["failure", "interruption", "stale-attempt", "start-guard }), ), }), - Layer.succeed(RunFinalizationObserver, { + Layer.succeed(RunFinalizationService.RunFinalizationObserver, { refresh: () => Effect.void, refreshAfterTurn: () => Ref.update(refreshes, (count) => count + 1), }), @@ -849,7 +868,7 @@ for (const scenario of ["failure", "interruption", "stale-attempt", "start-guard ); const result = yield* Effect.gen(function* () { - const runExecution = yield* RunExecutionServiceV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; yield* runExecution.startRootRun({ commandId: CommandId.make("command:run-execution-settings-failure"), appThread: { id: threadId } as OrchestrationV2AppThread, @@ -971,11 +990,11 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", const subagentNodeId = NodeId.make("node:run-execution-late-child:subagent"); const childMessageIngested = yield* Deferred.make(); const order = yield* Ref.make>([]); - const testLayer = runExecutionServiceLayer.pipe( + const testLayer = RunExecutionService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ captureBaseline: () => Effect.void }), - Layer.mock(EventSinkV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), + Layer.mock(EventSink.EventSinkV2)({ write: () => Effect.succeed([]), writeWithEffects: (input) => Effect.gen(function* () { @@ -990,8 +1009,8 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", }), writeIfRunCurrent: () => Effect.succeed({ committed: true, storedEvents: [] }), }), - idAllocatorLayer, - Layer.mock(ProviderEventIngestorV2)({ + IdAllocator.layer, + Layer.mock(ProviderEventIngestor.ProviderEventIngestorV2)({ ingestNormalized: (input) => Effect.gen(function* () { if ( @@ -1004,7 +1023,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", return []; }), }), - ServerSettingsService.layerTest(), + ServerSettings.layerTest(), ), ), ); @@ -1099,7 +1118,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", ]; yield* Effect.gen(function* () { - const runExecution = yield* RunExecutionServiceV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; yield* runExecution.startRootRun({ commandId: CommandId.make("command:run-execution-late-child"), appThread: { id: threadId } as OrchestrationV2AppThread, @@ -1294,7 +1313,7 @@ it.effect("does not hold the live stream open for a foreign provider's backgroun type: "subagent", status: "running", } as OrchestrationV2TurnItem; - const inherited = selectInheritedBackgroundTurnItems({ + const inherited = RunExecutionService.selectInheritedBackgroundTurnItems({ threadId: ids.threadId, currentProviderThreadId: ids.providerThreadId, currentRunOrdinal: 2, @@ -1332,7 +1351,7 @@ it.effect("refreshes inherited background items after event subscription", () => const loadInheritedBackgroundTurnItems = () => Ref.get(itemStatus).pipe( Effect.map((status) => - selectInheritedBackgroundTurnItems({ + RunExecutionService.selectInheritedBackgroundTurnItems({ threadId: ids.threadId, currentProviderThreadId: ids.providerThreadId, currentRunOrdinal: 2, @@ -1396,11 +1415,13 @@ it.effect( }> >([]); const ingestionDone = yield* Deferred.make(); - const testLayer = runExecutionServiceLayer.pipe( + const testLayer = RunExecutionService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ captureBaseline: () => Effect.void }), - Layer.mock(EventSinkV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ + captureBaseline: () => Effect.void, + }), + Layer.mock(EventSink.EventSinkV2)({ write: () => Effect.succeed([]), writeWithEffects: (input) => Effect.gen(function* () { @@ -1417,8 +1438,8 @@ it.effect( writeIfProviderThreadOwner: () => Effect.succeed({ committed: true, storedEvents: [] }), }), - idAllocatorLayer, - Layer.mock(ProviderEventIngestorV2)({ + IdAllocator.layer, + Layer.mock(ProviderEventIngestor.ProviderEventIngestorV2)({ ingestNormalized: (input) => Effect.gen(function* () { const event = input.event; @@ -1453,7 +1474,7 @@ it.effect( return []; }), }), - ServerSettingsService.layerTest(), + ServerSettings.layerTest(), ), ), ); @@ -1477,7 +1498,7 @@ it.effect( }; yield* Effect.gen(function* () { - const runExecution = yield* RunExecutionServiceV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; yield* runExecution.startRootRun({ commandId: CommandId.make(`command:${key}`), appThread: { id: ids.threadId } as OrchestrationV2AppThread, @@ -1611,11 +1632,11 @@ it.effect("drops late root provider-thread writes from a superseded attempt", () readonly committed: boolean; }> >([]); - const testLayer = runExecutionServiceLayer.pipe( + const testLayer = RunExecutionService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ captureBaseline: () => Effect.void }), - Layer.mock(EventSinkV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), + Layer.mock(EventSink.EventSinkV2)({ write: () => Effect.succeed([]), writeWithEffects: (input) => Effect.gen(function* () { @@ -1632,8 +1653,8 @@ it.effect("drops late root provider-thread writes from a superseded attempt", () writeIfProviderThreadOwner: () => Effect.succeed({ committed: false, storedEvents: [] }), }), - idAllocatorLayer, - Layer.mock(ProviderEventIngestorV2)({ + IdAllocator.layer, + Layer.mock(ProviderEventIngestor.ProviderEventIngestorV2)({ ingestNormalized: (input) => Effect.gen(function* () { const event = input.event; @@ -1670,7 +1691,7 @@ it.effect("drops late root provider-thread writes from a superseded attempt", () return []; }), }), - ServerSettingsService.layerTest(), + ServerSettings.layerTest(), ), ), ); @@ -1694,7 +1715,7 @@ it.effect("drops late root provider-thread writes from a superseded attempt", () }; yield* Effect.gen(function* () { - const runExecution = yield* RunExecutionServiceV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; yield* runExecution.startRootRun({ commandId: CommandId.make(`command:${key}`), appThread: { id: ids.threadId } as OrchestrationV2AppThread, @@ -1815,11 +1836,13 @@ it.effect( // Probe stays true forever; open background items must pin the stream // past ownership-loss so late turn_item completions still land. const ingestionDone = yield* Deferred.make(); - const testLayer = runExecutionServiceLayer.pipe( + const testLayer = RunExecutionService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ captureBaseline: () => Effect.void }), - Layer.mock(EventSinkV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ + captureBaseline: () => Effect.void, + }), + Layer.mock(EventSink.EventSinkV2)({ write: () => Effect.succeed([]), writeWithEffects: (input) => Effect.gen(function* () { @@ -1836,8 +1859,8 @@ it.effect( writeIfProviderThreadOwner: () => Effect.succeed({ committed: false, storedEvents: [] }), }), - idAllocatorLayer, - Layer.mock(ProviderEventIngestorV2)({ + IdAllocator.layer, + Layer.mock(ProviderEventIngestor.ProviderEventIngestorV2)({ ingestNormalized: (input) => Effect.gen(function* () { const event = input.event; @@ -1860,7 +1883,7 @@ it.effect( return []; }), }), - ServerSettingsService.layerTest(), + ServerSettings.layerTest(), ), ), ); @@ -1884,7 +1907,7 @@ it.effect( }; yield* Effect.gen(function* () { - const runExecution = yield* RunExecutionServiceV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; yield* runExecution.startRootRun({ commandId: CommandId.make(`command:${key}`), appThread: { id: ids.threadId } as OrchestrationV2AppThread, @@ -1984,11 +2007,13 @@ it.effect( const observed = yield* Ref.make>([]); const ingestionDone = yield* Deferred.make(); const scopedProbeArgs = yield* Ref.make>([]); - const testLayer = runExecutionServiceLayer.pipe( + const testLayer = RunExecutionService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ captureBaseline: () => Effect.void }), - Layer.mock(EventSinkV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ + captureBaseline: () => Effect.void, + }), + Layer.mock(EventSink.EventSinkV2)({ write: () => Effect.succeed([]), writeWithEffects: (input) => Effect.gen(function* () { @@ -2003,8 +2028,8 @@ it.effect( }), writeIfRunCurrent: () => Effect.succeed({ committed: true, storedEvents: [] }), }), - idAllocatorLayer, - Layer.mock(ProviderEventIngestorV2)({ + IdAllocator.layer, + Layer.mock(ProviderEventIngestor.ProviderEventIngestorV2)({ ingestNormalized: (input) => Effect.gen(function* () { if (input.event.type === "turn.terminal") { @@ -2013,7 +2038,7 @@ it.effect( return []; }), }), - ServerSettingsService.layerTest(), + ServerSettings.layerTest(), ), ), ); @@ -2042,7 +2067,7 @@ it.effect( const siblingProviderThreadId = ProviderThreadId.make(`provider-thread:${key}:sibling`); yield* Effect.gen(function* () { - const runExecution = yield* RunExecutionServiceV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; yield* runExecution.startRootRun({ commandId: CommandId.make(`command:${key}`), appThread: { id: ids.threadId } as OrchestrationV2AppThread, @@ -2151,11 +2176,13 @@ it.effect( const written = yield* Ref.make>([]); const ingested = yield* Ref.make>([]); const ingestionDone = yield* Deferred.make(); - const testLayer = runExecutionServiceLayer.pipe( + const testLayer = RunExecutionService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ captureBaseline: () => Effect.void }), - Layer.mock(EventSinkV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ + captureBaseline: () => Effect.void, + }), + Layer.mock(EventSink.EventSinkV2)({ write: () => Effect.succeed([]), writeWithEffects: (input) => Effect.gen(function* () { @@ -2164,12 +2191,12 @@ it.effect( }), writeIfRunCurrent: () => Effect.succeed({ committed: true, storedEvents: [] }), }), - idAllocatorLayer, - Layer.mock(ProviderEventIngestorV2)({ + IdAllocator.layer, + Layer.mock(ProviderEventIngestor.ProviderEventIngestorV2)({ ingestNormalized: (input) => Ref.update(ingested, (current) => [...current, input.event]).pipe(Effect.as([])), }), - ServerSettingsService.layerTest(), + ServerSettings.layerTest(), ), ), ); @@ -2224,7 +2251,7 @@ it.effect( }; yield* Effect.gen(function* () { - const runExecution = yield* RunExecutionServiceV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; yield* runExecution.startRootRun({ commandId: CommandId.make("command:subagent-interrupt-cascade"), appThread: { id: ids.threadId } as OrchestrationV2AppThread, @@ -2522,11 +2549,13 @@ it.effect( const providerInstanceId = ProviderInstanceId.make("codex"); const written = yield* Ref.make>([]); const ingestionDone = yield* Deferred.make(); - const testLayer = runExecutionServiceLayer.pipe( + const testLayer = RunExecutionService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ captureBaseline: () => Effect.void }), - Layer.mock(EventSinkV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ + captureBaseline: () => Effect.void, + }), + Layer.mock(EventSink.EventSinkV2)({ write: () => Effect.succeed([]), writeWithEffects: (input) => Effect.gen(function* () { @@ -2535,11 +2564,11 @@ it.effect( }), writeIfRunCurrent: () => Effect.succeed({ committed: true, storedEvents: [] }), }), - idAllocatorLayer, - Layer.mock(ProviderEventIngestorV2)({ + IdAllocator.layer, + Layer.mock(ProviderEventIngestor.ProviderEventIngestorV2)({ ingestNormalized: () => Effect.succeed([]), }), - ServerSettingsService.layerTest(), + ServerSettings.layerTest(), ), ), ); @@ -2576,7 +2605,7 @@ it.effect( const completedAt = runningSubagent.updatedAt; yield* Effect.gen(function* () { - const runExecution = yield* RunExecutionServiceV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; yield* runExecution.startRootRun({ commandId: CommandId.make("command:subagent-link-survives-terminal"), appThread: { id: ids.threadId } as OrchestrationV2AppThread, @@ -2889,7 +2918,7 @@ it.effect("cascade helper is provider-neutral for Claude and Codex-shaped child input: "sleep 300", }; - const events = yield* cascadeTerminalizeRunOwnedSubagents({ + const events = yield* RunExecutionService.cascadeTerminalizeRunOwnedSubagents({ run: { id: runId, threadId, @@ -2936,24 +2965,25 @@ it.effect("cascade helper is provider-neutral for Claude and Codex-shaped child // Shared cascade path: after subagent/turn-item rows are gone, only the // preserved linkage may prove an open child-thread node is cascadeable. - const afterTerminalLinkEvents = yield* cascadeTerminalizeRunOwnedSubagents({ - run: { - id: runId, - threadId, - ordinal: 1, - providerInstanceId, - } as OrchestrationV2Run, - open: { - subagents: new Map(), - turnItems: new Map(), - childTurnItems: new Map([[childTurnItem.id, childTurnItem]]), - nodes: new Map([[childNodeId, openChildNode]]), - linkedChildThreadIds: new Set([childThreadId]), - }, - status: terminalStatus, - completedAt: now, - allocateEventId, - }); + const afterTerminalLinkEvents = + yield* RunExecutionService.cascadeTerminalizeRunOwnedSubagents({ + run: { + id: runId, + threadId, + ordinal: 1, + providerInstanceId, + } as OrchestrationV2Run, + open: { + subagents: new Map(), + turnItems: new Map(), + childTurnItems: new Map([[childTurnItem.id, childTurnItem]]), + nodes: new Map([[childNodeId, openChildNode]]), + linkedChildThreadIds: new Set([childThreadId]), + }, + status: terminalStatus, + completedAt: now, + allocateEventId, + }); assert.equal( afterTerminalLinkEvents.length, 2, @@ -3018,12 +3048,12 @@ it.effect("emits run_interrupt_result when superseded attempt still has a hard-s assert.deepEqual(observed, ["pull-requests-refreshed"]); const ids = backgroundScenarioIds("stop-then-steer-supersede"); const expectedRequestId = yield* Effect.gen(function* () { - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; return idAllocator.derive.runSignalTurnItem({ runId: ids.runId, signal: "interrupt-request", }); - }).pipe(Effect.provide(idAllocatorLayer)); + }).pipe(Effect.provide(IdAllocator.layer)); assert.equal(written[0]?.parentItemId, expectedRequestId); }), ); @@ -3186,11 +3216,11 @@ function captureRootRunTermination(input: { const ingestionDone = yield* Deferred.make(); const captureTurnItem = (payload: OrchestrationV2TurnItem) => Ref.update(writtenItems, (current) => [...current, payload]); - const testLayer = runExecutionServiceLayer.pipe( + const testLayer = RunExecutionService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ captureBaseline: () => Effect.void }), - Layer.mock(EventSinkV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), + Layer.mock(EventSink.EventSinkV2)({ write: (payload) => Effect.gen(function* () { for (const event of payload.events) { @@ -3217,12 +3247,12 @@ function captureRootRunTermination(input: { }), writeIfRunCurrent: () => Effect.succeed({ committed: true, storedEvents: [] }), }), - idAllocatorLayer, - Layer.mock(ProviderEventIngestorV2)({ + IdAllocator.layer, + Layer.mock(ProviderEventIngestor.ProviderEventIngestorV2)({ ingestNormalized: () => Effect.succeed([]), }), - ServerSettingsService.layerTest(), - Layer.succeed(RunFinalizationObserver, { + ServerSettings.layerTest(), + Layer.succeed(RunFinalizationService.RunFinalizationObserver, { refresh: () => Effect.void, refreshAfterTurn: () => Ref.update(observed, (current) => [...current, "pull-requests-refreshed"]).pipe( @@ -3234,7 +3264,7 @@ function captureRootRunTermination(input: { ); yield* Effect.gen(function* () { - const runExecution = yield* RunExecutionServiceV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; yield* runExecution.startRootRun({ commandId: CommandId.make(`command:${input.key}`), appThread: { id: ids.threadId } as OrchestrationV2AppThread, @@ -3657,11 +3687,11 @@ function runBackgroundItemScenario( const providerInstanceId = ProviderInstanceId.make("codex"); const observed = yield* Ref.make>([]); const ingestionDone = yield* Deferred.make(); - const testLayer = runExecutionServiceLayer.pipe( + const testLayer = RunExecutionService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(CheckpointServiceV2)({ captureBaseline: () => Effect.void }), - Layer.mock(EventSinkV2)({ + Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), + Layer.mock(EventSink.EventSinkV2)({ write: () => Effect.succeed([]), writeWithEffects: (input) => Effect.gen(function* () { @@ -3676,8 +3706,8 @@ function runBackgroundItemScenario( }), writeIfRunCurrent: () => Effect.succeed({ committed: true, storedEvents: [] }), }), - idAllocatorLayer, - Layer.mock(ProviderEventIngestorV2)({ + IdAllocator.layer, + Layer.mock(ProviderEventIngestor.ProviderEventIngestorV2)({ ingestNormalized: (input) => Effect.gen(function* () { const event = input.event; @@ -3696,13 +3726,13 @@ function runBackgroundItemScenario( return []; }), }), - ServerSettingsService.layerTest(), + ServerSettings.layerTest(), ), ), ); yield* Effect.gen(function* () { - const runExecution = yield* RunExecutionServiceV2; + const runExecution = yield* RunExecutionService.RunExecutionServiceV2; yield* runExecution.startRootRun({ commandId: CommandId.make(`command:${key}`), appThread: { id: ids.threadId } as OrchestrationV2AppThread, diff --git a/apps/server/src/orchestration-v2/RunExecutionService.ts b/apps/server/src/orchestration-v2/RunExecutionService.ts index 1bc7a28d0e73..65af5aa6f9ac 100644 --- a/apps/server/src/orchestration-v2/RunExecutionService.ts +++ b/apps/server/src/orchestration-v2/RunExecutionService.ts @@ -35,14 +35,10 @@ import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; -import { CheckpointServiceV2 } from "./CheckpointService.ts"; -import { EventSinkV2 } from "./EventSink.ts"; -import { - IdAllocatorV2, - type IdAllocatorV2AllocationError, - type IdAllocatorV2Shape, -} from "./IdAllocator.ts"; +import * as ServerSettings from "../serverSettings.ts"; +import * as CheckpointService from "./CheckpointService.ts"; +import * as EventSink from "./EventSink.ts"; +import * as IdAllocator from "./IdAllocator.ts"; import type { ProviderAdapterV2Event, ProviderAdapterV2RuntimePolicy, @@ -50,9 +46,9 @@ import type { ProviderAdapterV2TurnMessage, } from "./ProviderAdapter.ts"; import { ProviderAdapterTurnStartError } from "./ProviderAdapter.ts"; -import { ProviderEventIngestorV2 } from "./ProviderEventIngestor.ts"; +import * as ProviderEventIngestor from "./ProviderEventIngestor.ts"; import { makeProviderFailure, makeProviderFailureTurnItem } from "./ProviderFailure.ts"; -import { RunFinalizationObserver } from "./RunFinalizationService.ts"; +import * as RunFinalizationService from "./RunFinalizationService.ts"; export interface ProviderEventRoutingState { readonly ownedThreadIds: ReadonlySet; @@ -200,8 +196,11 @@ export function cascadeTerminalizeRunOwnedSubagents(input: { readonly open: OpenRunOwnedSubagentProjection; readonly status: RunOwnedSubagentTerminalStatus; readonly completedAt: DateTime.Utc; - readonly allocateEventId: () => Effect.Effect; -}): Effect.Effect, IdAllocatorV2AllocationError> { + readonly allocateEventId: () => Effect.Effect; +}): Effect.Effect< + ReadonlyArray, + IdAllocator.IdAllocatorV2AllocationError +> { return Effect.gen(function* () { const events: Array = []; // Prefer lifetime linkage over currently-open rows: subagent/turn-item @@ -524,20 +523,20 @@ export class RunExecutionServiceV2 extends Context.Service< export const layer: Layer.Layer< RunExecutionServiceV2, never, - | CheckpointServiceV2 - | EventSinkV2 - | IdAllocatorV2 - | ProviderEventIngestorV2 - | ServerSettingsService + | CheckpointService.CheckpointServiceV2 + | EventSink.EventSinkV2 + | IdAllocator.IdAllocatorV2 + | ProviderEventIngestor.ProviderEventIngestorV2 + | ServerSettings.ServerSettingsService > = Layer.effect( RunExecutionServiceV2, Effect.gen(function* () { - const checkpointService = yield* CheckpointServiceV2; - const eventSink = yield* EventSinkV2; - const idAllocator = yield* IdAllocatorV2; - const providerEventIngestor = yield* ProviderEventIngestorV2; - const serverSettings = yield* ServerSettingsService; - const finalizationObserver = yield* RunFinalizationObserver; + const checkpointService = yield* CheckpointService.CheckpointServiceV2; + const eventSink = yield* EventSink.EventSinkV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const providerEventIngestor = yield* ProviderEventIngestor.ProviderEventIngestorV2; + const serverSettings = yield* ServerSettings.ServerSettingsService; + const finalizationObserver = yield* RunFinalizationService.RunFinalizationObserver; const writeFinalRunEvents = (input: { readonly run: OrchestrationV2Run; @@ -1422,7 +1421,7 @@ export const layer: Layer.Layer< ); function makeInterruptResultTurnItem(input: { - readonly idAllocator: IdAllocatorV2Shape; + readonly idAllocator: IdAllocator.IdAllocatorV2Shape; readonly run: OrchestrationV2Run; readonly rootNode: OrchestrationV2ExecutionNode; readonly providerThread: OrchestrationV2ProviderThread; diff --git a/apps/server/src/orchestration-v2/RuntimePolicy.test.ts b/apps/server/src/orchestration-v2/RuntimePolicy.test.ts index df50e6d3bae6..ff18b210e7bc 100644 --- a/apps/server/src/orchestration-v2/RuntimePolicy.test.ts +++ b/apps/server/src/orchestration-v2/RuntimePolicy.test.ts @@ -15,9 +15,9 @@ import * as Option from "effect/Option"; import * as Stream from "effect/Stream"; import type { ProviderInstance } from "../provider/ProviderDriver.ts"; -import { ProviderInstanceRegistry } from "../provider/Services/ProviderInstanceRegistry.ts"; +import * as ProviderInstanceRegistry from "../provider/Services/ProviderInstanceRegistry.ts"; import * as ProjectStore from "./ProjectStore.ts"; -import { layerFromProjectStore, RuntimePolicyV2 } from "./RuntimePolicy.ts"; +import * as RuntimePolicy from "./RuntimePolicy.ts"; const projectId = ProjectId.make("project:runtime-policy"); const providerInstanceId = ProviderInstanceId.make("codex"); @@ -76,9 +76,9 @@ const providerInstanceFor = (instanceId: ProviderInstanceId) => }, }) as ProviderInstance; -const TestLayer = layerFromProjectStore.pipe( +const TestLayer = RuntimePolicy.layerFromProjectStore.pipe( Layer.provide( - Layer.succeed(ProviderInstanceRegistry, { + Layer.succeed(ProviderInstanceRegistry.ProviderInstanceRegistry, { getInstance: (instanceId) => Effect.succeed(providerInstanceFor(instanceId)), listInstances: Effect.succeed([]), listUnavailable: Effect.succeed([]), @@ -112,7 +112,7 @@ const TestLayer = layerFromProjectStore.pipe( it.layer(TestLayer)("RuntimePolicyV2", (it) => { it.effect("uses the project root for local-checkout threads", () => Effect.gen(function* () { - const policy = yield* RuntimePolicyV2; + const policy = yield* RuntimePolicy.RuntimePolicyV2; const now = yield* DateTime.now; const resolved = yield* policy.resolve({ thread: makeThread({ now, worktreePath: null }), @@ -124,7 +124,7 @@ it.layer(TestLayer)("RuntimePolicyV2", (it) => { it.effect("prefers a provisioned worktree over the project root", () => Effect.gen(function* () { - const policy = yield* RuntimePolicyV2; + const policy = yield* RuntimePolicy.RuntimePolicyV2; const now = yield* DateTime.now; const resolved = yield* policy.resolve({ thread: makeThread({ now, worktreePath: "/project-worktree" }), @@ -136,7 +136,7 @@ it.layer(TestLayer)("RuntimePolicyV2", (it) => { it.effect("runs a mode the provider does not offer in Supervised", () => Effect.gen(function* () { - const policy = yield* RuntimePolicyV2; + const policy = yield* RuntimePolicy.RuntimePolicyV2; const now = yield* DateTime.now; const modeFor = (instanceId: ProviderInstanceId, runtimeMode: RuntimeMode) => policy diff --git a/apps/server/src/orchestration-v2/RuntimePolicy.ts b/apps/server/src/orchestration-v2/RuntimePolicy.ts index ef420742dddb..0b0523a2c2fb 100644 --- a/apps/server/src/orchestration-v2/RuntimePolicy.ts +++ b/apps/server/src/orchestration-v2/RuntimePolicy.ts @@ -11,7 +11,7 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; -import { ProviderInstanceRegistry } from "../provider/Services/ProviderInstanceRegistry.ts"; +import * as ProviderInstanceRegistry from "../provider/Services/ProviderInstanceRegistry.ts"; import { ProviderAdapterV2RuntimePolicy, type ProviderAdapterV2RuntimePolicy as ProviderAdapterV2RuntimePolicyType, @@ -90,12 +90,12 @@ function providerRuntimeMode( export const layerFromProjectStore: Layer.Layer< RuntimePolicyV2, never, - ProjectStore.ProjectStoreV2 | ProviderInstanceRegistry + ProjectStore.ProjectStoreV2 | ProviderInstanceRegistry.ProviderInstanceRegistry > = Layer.effect( RuntimePolicyV2, Effect.gen(function* () { const projects = yield* ProjectStore.ProjectStoreV2; - const providerInstances = yield* ProviderInstanceRegistry; + const providerInstances = yield* ProviderInstanceRegistry.ProviderInstanceRegistry; return RuntimePolicyV2.of({ resolve: Effect.fn("RuntimePolicyV2.resolve")(function* (input) { const instance = yield* providerInstances.getInstance(input.modelSelection.instanceId); diff --git a/apps/server/src/orchestration-v2/RuntimeRequestService.test.ts b/apps/server/src/orchestration-v2/RuntimeRequestService.test.ts index dcf93a7ee93e..05306daddabb 100644 --- a/apps/server/src/orchestration-v2/RuntimeRequestService.test.ts +++ b/apps/server/src/orchestration-v2/RuntimeRequestService.test.ts @@ -13,17 +13,9 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import type { ProviderAdapterV2RuntimeRequestResponseInput } from "./ProviderAdapter.ts"; -import { ProjectionStoreV2 } from "./ProjectionStore.ts"; -import { - ProviderSessionLookupError, - ProviderSessionManagerV2, - type ProviderSessionManagerV2Shape, -} from "./ProviderSessionManager.ts"; -import { - layer as runtimeRequestServiceLayer, - RuntimeRequestResponseExecutionError, - RuntimeRequestServiceV2, -} from "./RuntimeRequestService.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; +import * as ProviderSessionManager from "./ProviderSessionManager.ts"; +import * as RuntimeRequestService from "./RuntimeRequestService.ts"; function projectionWithRuntimeRequest( runtimeRequest?: OrchestrationV2RuntimeRequest, @@ -55,16 +47,16 @@ function resolvedRuntimeRequest( function runtimeRequestTestLayer( projection: OrchestrationV2ThreadProjection, - getSession: ProviderSessionManagerV2Shape["get"], + getSession: ProviderSessionManager.ProviderSessionManagerV2Shape["get"], ) { - return runtimeRequestServiceLayer.pipe( + return RuntimeRequestService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(ProjectionStoreV2)({ + Layer.mock(ProjectionStore.ProjectionStoreV2)({ getRuntimeRequest: (_threadId, requestId) => Effect.succeed(projection.runtimeRequests.find((request) => request.id === requestId)), }), - Layer.mock(ProviderSessionManagerV2)({ + Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ get: getSession, }), ), @@ -104,14 +96,14 @@ it.effect("forwards orchestrator-resolved runtime requests to the live adapter", }, ], } as unknown as OrchestrationV2ThreadProjection; - const testLayer = runtimeRequestServiceLayer.pipe( + const testLayer = RuntimeRequestService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(ProjectionStoreV2)({ + Layer.mock(ProjectionStore.ProjectionStoreV2)({ getRuntimeRequest: (_threadId, requestId) => Effect.succeed(projection.runtimeRequests.find((request) => request.id === requestId)), }), - Layer.mock(ProviderSessionManagerV2)({ + Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ get: getSession, }), ), @@ -119,7 +111,7 @@ it.effect("forwards orchestrator-resolved runtime requests to the live adapter", ); return Effect.gen(function* () { - const service = yield* RuntimeRequestServiceV2; + const service = yield* RuntimeRequestService.RuntimeRequestServiceV2; yield* service.respond({ threadId, providerSessionId, @@ -168,14 +160,14 @@ it.effect("rejects expired runtime requests before invoking the live adapter", ( }, ], } as unknown as OrchestrationV2ThreadProjection; - const testLayer = runtimeRequestServiceLayer.pipe( + const testLayer = RuntimeRequestService.layer.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(ProjectionStoreV2)({ + Layer.mock(ProjectionStore.ProjectionStoreV2)({ getRuntimeRequest: (_threadId, requestId) => Effect.succeed(projection.runtimeRequests.find((request) => request.id === requestId)), }), - Layer.mock(ProviderSessionManagerV2)({ + Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ get: getSession, }), ), @@ -183,7 +175,7 @@ it.effect("rejects expired runtime requests before invoking the live adapter", ( ); return Effect.gen(function* () { - const service = yield* RuntimeRequestServiceV2; + const service = yield* RuntimeRequestService.RuntimeRequestServiceV2; const error = yield* service .respond({ threadId, @@ -225,7 +217,7 @@ it.effect("classifies runtime request response validation failures", () => { () => Effect.succeed(Option.none()), ); const respond = Effect.gen(function* () { - const service = yield* RuntimeRequestServiceV2; + const service = yield* RuntimeRequestService.RuntimeRequestServiceV2; return yield* service .respond({ threadId, @@ -270,7 +262,7 @@ it.effect("preserves genuine provider session lookup failures as the cause", () "provider-session-runtime-request-lookup-failure", ); const requestId = RuntimeRequestId.make("request-lookup-failure"); - const lookupFailure = new ProviderSessionLookupError({ + const lookupFailure = new ProviderSessionManager.ProviderSessionLookupError({ providerSessionId, cause: "lookup failed", }); @@ -280,7 +272,7 @@ it.effect("preserves genuine provider session lookup failures as the cause", () ); return Effect.gen(function* () { - const service = yield* RuntimeRequestServiceV2; + const service = yield* RuntimeRequestService.RuntimeRequestServiceV2; const error = yield* service .respond({ threadId, @@ -290,7 +282,7 @@ it.effect("preserves genuine provider session lookup failures as the cause", () }) .pipe(Effect.flip); - assert.instanceOf(error, RuntimeRequestResponseExecutionError); + assert.instanceOf(error, RuntimeRequestService.RuntimeRequestResponseExecutionError); assert.equal(error.reason, "unexpected-failure"); assert.equal( error.message, diff --git a/apps/server/src/orchestration-v2/RuntimeRequestService.ts b/apps/server/src/orchestration-v2/RuntimeRequestService.ts index a834b1b1fc6c..619768532711 100644 --- a/apps/server/src/orchestration-v2/RuntimeRequestService.ts +++ b/apps/server/src/orchestration-v2/RuntimeRequestService.ts @@ -11,8 +11,8 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; -import { ProjectionStoreV2 } from "./ProjectionStore.ts"; -import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; +import * as ProviderSessionManager from "./ProviderSessionManager.ts"; export class RuntimeRequestResponseExecutionError extends Schema.TaggedError()( "RuntimeRequestResponseExecutionError", @@ -66,12 +66,12 @@ export class RuntimeRequestServiceV2 extends Context.Service< export const layer: Layer.Layer< RuntimeRequestServiceV2, never, - ProjectionStoreV2 | ProviderSessionManagerV2 + ProjectionStore.ProjectionStoreV2 | ProviderSessionManager.ProviderSessionManagerV2 > = Layer.effect( RuntimeRequestServiceV2, Effect.gen(function* () { - const projections = yield* ProjectionStoreV2; - const sessions = yield* ProviderSessionManagerV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; + const sessions = yield* ProviderSessionManager.ProviderSessionManagerV2; return RuntimeRequestServiceV2.of({ respond: (input) => diff --git a/apps/server/src/orchestration-v2/SelectionRestart.integration.test.ts b/apps/server/src/orchestration-v2/SelectionRestart.integration.test.ts index d62e13ba64d9..414478b39061 100644 --- a/apps/server/src/orchestration-v2/SelectionRestart.integration.test.ts +++ b/apps/server/src/orchestration-v2/SelectionRestart.integration.test.ts @@ -25,20 +25,16 @@ import * as Ref from "effect/Ref"; import * as Stream from "effect/Stream"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; -import { OrchestrationEffectWorkerV2 } from "./EffectWorker.ts"; -import { EventSinkV2 } from "./EventSink.ts"; -import { OrchestratorV2 } from "./Orchestrator.ts"; +import * as EffectWorker from "./EffectWorker.ts"; +import * as EventSink from "./EventSink.ts"; +import * as Orchestrator from "./Orchestrator.ts"; import { ProviderAdapterOpenSessionError, type ProviderAdapterV2Event, type ProviderAdapterV2Shape, type ProviderAdapterV2TurnInput, } from "./ProviderAdapter.ts"; -import { - ProviderAdapterRegistryV2, - makeLayer as makeProviderAdapterRegistryLayer, - makeSingleLayer as makeSingleProviderAdapterRegistryLayer, -} from "./ProviderAdapterRegistry.ts"; +import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; import { checkpointWorkspace } from "./testkit/ReplayFixtureWorkspace.ts"; @@ -407,10 +403,10 @@ it.live("restarts selection as a new attempt and retries after old-session clean closedSessionCount: 0, failedReplacementOpen: false, }); - const registry = makeSingleProviderAdapterRegistryLayer(makeRestartAdapter(state)); + const registry = ProviderAdapterRegistry.makeSingleLayer(makeRestartAdapter(state)); const result = yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; yield* orchestrator.dispatch({ type: "thread.create", createdBy: "user", @@ -559,14 +555,14 @@ for (const deadStatus of ["stopped", "error"] as const) { // simulated replacement-open failure is skipped. failedReplacementOpen: true, }); - const registry = makeSingleProviderAdapterRegistryLayer( + const registry = ProviderAdapterRegistry.makeSingleLayer( makeRestartAdapter(state, exclusiveCapabilities), ); const result = yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const worker = yield* OrchestrationEffectWorkerV2; - const eventSink = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; + const eventSink = yield* EventSink.EventSinkV2; const dispatch = (step: string, modelSelection: ModelSelection) => Effect.gen(function* () { const terminal = yield* orchestrator.streamDomainEvents.pipe( @@ -725,13 +721,13 @@ it.live("detaches the old provider session after an active provider handoff", () failedReplacementOpen: false, }); const targetStartCount = yield* Ref.make(0); - const registry = makeProviderAdapterRegistryLayer([ + const registry = ProviderAdapterRegistry.makeLayer([ makeRestartAdapter(state, exclusiveCapabilities), makeCompletingHandoffAdapter(targetStartCount), ]); const result = yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; yield* orchestrator.dispatch({ type: "thread.create", createdBy: "user", @@ -872,7 +868,7 @@ for (const mode of ["active", "idle", "selection-command", "pooled", "separate-h ), } satisfies ProviderAdapterV2Shape; }); - const registry = Layer.succeed(ProviderAdapterRegistryV2, { + const registry = Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistryV2, { get: (instanceId) => Effect.succeed(adapters.find((adapter) => adapter.instanceId === instanceId)!), list: () => Effect.succeed([providerInstanceId, targetId]), @@ -886,8 +882,8 @@ for (const mode of ["active", "idle", "selection-command", "pooled", "separate-h }), }); yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const worker = yield* OrchestrationEffectWorkerV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; const selection = { ...initialSelection, model: mode === "active" ? initialSelection.model : "complete", diff --git a/apps/server/src/orchestration-v2/SteeringCompletion.integration.test.ts b/apps/server/src/orchestration-v2/SteeringCompletion.integration.test.ts index 750d2b4312bc..bfaf0bb476d1 100644 --- a/apps/server/src/orchestration-v2/SteeringCompletion.integration.test.ts +++ b/apps/server/src/orchestration-v2/SteeringCompletion.integration.test.ts @@ -19,16 +19,16 @@ import * as Fiber from "effect/Fiber"; import * as Queue from "effect/Queue"; import * as Stream from "effect/Stream"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; -import { OrchestrationEffectWorkerV2 } from "./EffectWorker.ts"; -import { EventSinkV2 } from "./EventSink.ts"; -import { OrchestratorV2 } from "./Orchestrator.ts"; +import * as EffectWorker from "./EffectWorker.ts"; +import * as EventSink from "./EventSink.ts"; +import * as Orchestrator from "./Orchestrator.ts"; import { ProviderAdapterSteerRunError, type ProviderAdapterV2Event, type ProviderAdapterV2Shape, type ProviderAdapterV2TurnInput, } from "./ProviderAdapter.ts"; -import { makeSingleLayer } from "./ProviderAdapterRegistry.ts"; +import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; import { checkpointWorkspace } from "./testkit/ReplayFixtureWorkspace.ts"; @@ -155,8 +155,8 @@ for (const mailbox of [false, true]) { }), }; yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const worker = yield* OrchestrationEffectWorkerV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; const threadId = ThreadId.make("thread:steering-completion"); const watch = (predicate: (event: OrchestrationV2DomainEvent) => boolean) => orchestrator.streamDomainEvents.pipe( @@ -200,7 +200,7 @@ for (const mailbox of [false, true]) { const messageId = MessageId.make("message:steering"); const taskId = NodeId.make("task:mailbox"); if (mailbox) { - const sink = yield* EventSinkV2; + const sink = yield* EventSink.EventSinkV2; const current = yield* orchestrator.getThreadProjection(threadId); const parentRun = current.runs.find((run) => run.id === first.runId)!; const now = yield* DateTime.now; @@ -391,7 +391,7 @@ for (const mailbox of [false, true]) { Effect.provide( makeOrchestratorV2ReplayLayerWithRegistry( { name: `steering-completion-${timing}` }, - makeSingleLayer(adapter), + ProviderAdapterRegistry.makeSingleLayer(adapter), { runEffectWorker: false }, ), ), diff --git a/apps/server/src/orchestration-v2/ThreadDeletion.test.ts b/apps/server/src/orchestration-v2/ThreadDeletion.test.ts index 4f79fe01b764..d9a87e0df757 100644 --- a/apps/server/src/orchestration-v2/ThreadDeletion.test.ts +++ b/apps/server/src/orchestration-v2/ThreadDeletion.test.ts @@ -20,7 +20,7 @@ import * as DateTime from "effect/DateTime"; import * as Effect from "effect/Effect"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; -import { IdAllocatorV2, layer as idAllocatorLayer } from "./IdAllocator.ts"; +import * as IdAllocator from "./IdAllocator.ts"; import { applyToProjection, emptyProjection } from "./ProjectionStore.ts"; import { planThreadDeletion } from "./ThreadDeletion.ts"; @@ -186,7 +186,7 @@ it.effect("cancels active work without reviving a run while disposing delegated message.attachments.map((attachment) => attachment.id), ), now: deletedAt, - idAllocator: yield* IdAllocatorV2, + idAllocator: yield* IdAllocator.IdAllocatorV2, }); const deleted = plan.events.reduce(applyToProjection, projection); assert.deepEqual(deleted.thread.deletedAt, deletedAt); @@ -222,7 +222,7 @@ it.effect("cancels active work without reviving a run while disposing delegated (event) => event.type === "run.updated" && event.payload.id === queuedRun.id, ); assert.lengthOf(queuedRunUpdates, 1); - }).pipe(Effect.provide(idAllocatorLayer)), + }).pipe(Effect.provide(IdAllocator.layer)), ); it.effect("queues provider and resource cleanup and preserves an earlier deletion timestamp", () => @@ -273,7 +273,7 @@ it.effect("queues provider and resource cleanup and preserves an earlier deletio message.attachments.map((attachment) => attachment.id), ), now: deletedAt, - idAllocator: yield* IdAllocatorV2, + idAllocator: yield* IdAllocator.IdAllocatorV2, }); const deleted = plan.events.reduce(applyToProjection, projection); assert.deepEqual(deleted.thread.deletedAt, createdAt); @@ -294,5 +294,5 @@ it.effect("queues provider and resource cleanup and preserves an earlier deletio { type: "attachment.cleanup", attachmentIds: ["shared_file"] }, ], ); - }).pipe(Effect.provide(idAllocatorLayer)), + }).pipe(Effect.provide(IdAllocator.layer)), ); diff --git a/apps/server/src/orchestration-v2/ThreadFork.execution.test.ts b/apps/server/src/orchestration-v2/ThreadFork.execution.test.ts index 2f86f5946329..63bc1550b205 100644 --- a/apps/server/src/orchestration-v2/ThreadFork.execution.test.ts +++ b/apps/server/src/orchestration-v2/ThreadFork.execution.test.ts @@ -19,8 +19,8 @@ import * as Effect from "effect/Effect"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; import { ClaudeProviderCapabilitiesV2 } from "./Adapters/ClaudeAdapterV2.ts"; -import { EventSinkV2 } from "./EventSink.ts"; -import { OrchestratorV2 } from "./Orchestrator.ts"; +import * as EventSink from "./EventSink.ts"; +import * as Orchestrator from "./Orchestrator.ts"; import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; @@ -48,8 +48,8 @@ for (const driverName of ["codex", "claudeAgent"] as const) { for (const status of ["failed", "interrupted", "cancelled"] as const) { it.effect(`bounds ${driver} context when continuing a fork of a ${status} run`, () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; const now = yield* DateTime.now; const sourceThreadId = ThreadId.make("fork-boundary-source"); const targetThreadId = ThreadId.make("fork-boundary-target"); diff --git a/apps/server/src/orchestration-v2/ThreadForkService.test.ts b/apps/server/src/orchestration-v2/ThreadForkService.test.ts index f734dfdb68db..d9b38467de3f 100644 --- a/apps/server/src/orchestration-v2/ThreadForkService.test.ts +++ b/apps/server/src/orchestration-v2/ThreadForkService.test.ts @@ -15,12 +15,7 @@ import { import * as DateTime from "effect/DateTime"; import * as Effect from "effect/Effect"; -import { - forkableSourceRunStatusError, - isForkableSourceRunStatus, - layer, - ThreadForkServiceV2, -} from "./ThreadForkService.ts"; +import * as ThreadForkService from "./ThreadForkService.ts"; const sourceThreadId = ThreadId.make("thread:fork-snoozed-source"); const targetThreadId = ThreadId.make("thread:fork-awake-target"); @@ -113,7 +108,7 @@ function makeSourceProjection(sourceRun: OrchestrationV2Run): OrchestrationV2Thr const planFork = (sourceRun: OrchestrationV2Run) => Effect.gen(function* () { - const service = yield* ThreadForkServiceV2; + const service = yield* ThreadForkService.ThreadForkServiceV2; return yield* service.plan({ sourceProjection: makeSourceProjection(sourceRun), sourceRun, @@ -129,19 +124,19 @@ const planFork = (sourceRun: OrchestrationV2Run) => creationSource: "mobile", createdAt: forkCreatedAt, }); - }).pipe(Effect.provide(layer)); + }).pipe(Effect.provide(ThreadForkService.layer)); it("treats usage-limited and other provider-finished runs as forkable", () => { - assert.isTrue(isForkableSourceRunStatus("completed")); - assert.isTrue(isForkableSourceRunStatus("waiting")); - assert.isTrue(isForkableSourceRunStatus("failed")); - assert.isTrue(isForkableSourceRunStatus("interrupted")); - assert.isTrue(isForkableSourceRunStatus("cancelled")); - assert.isFalse(isForkableSourceRunStatus("running")); - assert.isFalse(isForkableSourceRunStatus("starting")); - assert.isFalse(isForkableSourceRunStatus("queued")); - assert.isFalse(isForkableSourceRunStatus("preparing")); - assert.isFalse(isForkableSourceRunStatus("rolled_back")); + assert.isTrue(ThreadForkService.isForkableSourceRunStatus("completed")); + assert.isTrue(ThreadForkService.isForkableSourceRunStatus("waiting")); + assert.isTrue(ThreadForkService.isForkableSourceRunStatus("failed")); + assert.isTrue(ThreadForkService.isForkableSourceRunStatus("interrupted")); + assert.isTrue(ThreadForkService.isForkableSourceRunStatus("cancelled")); + assert.isFalse(ThreadForkService.isForkableSourceRunStatus("running")); + assert.isFalse(ThreadForkService.isForkableSourceRunStatus("starting")); + assert.isFalse(ThreadForkService.isForkableSourceRunStatus("queued")); + assert.isFalse(ThreadForkService.isForkableSourceRunStatus("preparing")); + assert.isFalse(ThreadForkService.isForkableSourceRunStatus("rolled_back")); }); it.effect("keeps a fork awake when its source thread is snoozed", () => @@ -192,7 +187,7 @@ it.effect("rejects in-progress and rolled-back fork sources", () => assert.equal(error._tag, "ThreadForkPlanError"); assert.equal(error.sourceThreadId, sourceThreadId); assert.equal(error.targetThreadId, targetThreadId); - assert.equal(error.cause, forkableSourceRunStatusError(sourceRun)); + assert.equal(error.cause, ThreadForkService.forkableSourceRunStatusError(sourceRun)); } }), ); diff --git a/apps/server/src/orchestration-v2/ThreadManagementService.test.ts b/apps/server/src/orchestration-v2/ThreadManagementService.test.ts index 55873472291b..1df1c242700f 100644 --- a/apps/server/src/orchestration-v2/ThreadManagementService.test.ts +++ b/apps/server/src/orchestration-v2/ThreadManagementService.test.ts @@ -18,26 +18,9 @@ import * as Fiber from "effect/Fiber"; import * as Layer from "effect/Layer"; import * as TestClock from "effect/testing/TestClock"; -import { - LegacyV1ThreadImporter, - LegacyV1ThreadImportError, -} from "./legacy/LegacyV1ThreadImporter.ts"; -import { OrchestratorProjectionError, OrchestratorV2 } from "./Orchestrator.ts"; -import { - existingThreadIdsForCommand, - layer, - layerWithLegacyImporter, - ThreadManagementDurableRunProjectionError, - ThreadManagementProjectThreadsListError, - ThreadManagementProjectionLoadError, - ThreadManagementRunNotFoundError, - ThreadManagementService, - ThreadManagementThreadNotFoundError, - ThreadManagementThreadNotInterruptibleError, - ThreadManagementThreadArchivedError, - ThreadManagementNoSteerableRunError, - withCreationProvenance, -} from "./ThreadManagementService.ts"; +import * as LegacyV1ThreadImporter from "./legacy/LegacyV1ThreadImporter.ts"; +import * as Orchestrator from "./Orchestrator.ts"; +import * as ThreadManagementService from "./ThreadManagementService.ts"; it("stamps authoritative provenance on commands that create threads or messages", () => { const command: OrchestrationV2Command = { @@ -59,7 +42,7 @@ it("stamps authoritative provenance on commands that create threads or messages" }; expect( - withCreationProvenance(command, { + ThreadManagementService.withCreationProvenance(command, { createdBy: "user", creationSource: "web", }), @@ -78,7 +61,7 @@ it("leaves commands that do not create durable authored content unchanged", () = }; expect( - withCreationProvenance(command, { + ThreadManagementService.withCreationProvenance(command, { createdBy: "user", creationSource: "web", }), @@ -91,7 +74,7 @@ it("identifies every existing thread that must be hydrated before dispatch", () const parentThreadId = ThreadId.make("thread:thread-management:parent"); expect( - existingThreadIdsForCommand({ + ThreadManagementService.existingThreadIdsForCommand({ type: "thread.create", createdBy: "user", creationSource: "web", @@ -111,7 +94,7 @@ it("identifies every existing thread that must be hydrated before dispatch", () ).toEqual([]); expect( - existingThreadIdsForCommand({ + ThreadManagementService.existingThreadIdsForCommand({ type: "thread.archive", commandId: CommandId.make("command:thread-management:archive"), threadId: targetThreadId, @@ -121,7 +104,7 @@ it("identifies every existing thread that must be hydrated before dispatch", () // Read-state commands skip transcript hydration entirely: they fire on // every activity bump while a thread is open and never touch messages. expect( - existingThreadIdsForCommand({ + ThreadManagementService.existingThreadIdsForCommand({ type: "thread.visit", commandId: CommandId.make("command:thread-management:visit"), threadId: targetThreadId, @@ -130,7 +113,7 @@ it("identifies every existing thread that must be hydrated before dispatch", () ).toEqual([]); expect( - existingThreadIdsForCommand({ + ThreadManagementService.existingThreadIdsForCommand({ type: "thread.mark-unread", commandId: CommandId.make("command:thread-management:mark-unread"), threadId: targetThreadId, @@ -138,7 +121,7 @@ it("identifies every existing thread that must be hydrated before dispatch", () ).toEqual([]); expect( - existingThreadIdsForCommand({ + ThreadManagementService.existingThreadIdsForCommand({ type: "thread.fork", createdBy: "user", creationSource: "web", @@ -153,7 +136,7 @@ it("identifies every existing thread that must be hydrated before dispatch", () ).toEqual([sourceThreadId]); expect( - existingThreadIdsForCommand({ + ThreadManagementService.existingThreadIdsForCommand({ type: "thread.merge_back", createdBy: "user", creationSource: "web", @@ -168,7 +151,7 @@ it("identifies every existing thread that must be hydrated before dispatch", () ).toEqual([sourceThreadId, targetThreadId]); expect( - existingThreadIdsForCommand({ + ThreadManagementService.existingThreadIdsForCommand({ type: "delegated_task.request", createdBy: "agent", creationSource: "provider", @@ -187,7 +170,7 @@ it("identifies every existing thread that must be hydrated before dispatch", () ).toEqual([parentThreadId]); expect( - existingThreadIdsForCommand({ + ThreadManagementService.existingThreadIdsForCommand({ type: "delegated_task.wake-policy", commandId: CommandId.make("command:thread-management:wake-policy"), parentThreadId, @@ -197,7 +180,7 @@ it("identifies every existing thread that must be hydrated before dispatch", () ).toEqual([parentThreadId]); expect( - existingThreadIdsForCommand({ + ThreadManagementService.existingThreadIdsForCommand({ type: "thread.created.record", commandId: CommandId.make("command:thread-management:record"), parentThreadId, @@ -216,24 +199,27 @@ it("derives thread management messages from structural error attributes", () => const messageId = MessageId.make("message:thread-management:errors"); const infrastructureCause = new Error("private sqlite detail"); - const threadNotFound = new ThreadManagementThreadNotFoundError({ + const threadNotFound = new ThreadManagementService.ThreadManagementThreadNotFoundError({ projectId, threadId, }); expect(threadNotFound).toMatchObject({ projectId, threadId }); expect(threadNotFound.message).toBe(`Thread ${threadId} was not found in project ${projectId}.`); - const runNotFound = new ThreadManagementRunNotFoundError({ threadId, runId }); + const runNotFound = new ThreadManagementService.ThreadManagementRunNotFoundError({ + threadId, + runId, + }); expect(runNotFound).toMatchObject({ threadId, runId }); expect(runNotFound.message).toBe(`Run ${runId} does not belong to thread ${threadId}.`); - const archived = new ThreadManagementThreadArchivedError({ + const archived = new ThreadManagementService.ThreadManagementThreadArchivedError({ threadId, }); expect(archived).toMatchObject({ threadId }); expect(archived.message).toBe(`Thread ${threadId} is archived and cannot receive messages.`); - const notSteerable = new ThreadManagementNoSteerableRunError({ + const notSteerable = new ThreadManagementService.ThreadManagementNoSteerableRunError({ threadId, mode: "restart", }); @@ -245,14 +231,14 @@ it("derives thread management messages from structural error attributes", () => `Thread ${threadId} has no running turn that can be restarted.`, ); - const notInterruptible = new ThreadManagementThreadNotInterruptibleError({ + const notInterruptible = new ThreadManagementService.ThreadManagementThreadNotInterruptibleError({ threadId, runId, }); expect(notInterruptible).toMatchObject({ threadId, runId }); expect(notInterruptible.message).toBe(`Run ${runId} is not currently interruptible.`); - const listFailure = new ThreadManagementProjectThreadsListError({ + const listFailure = new ThreadManagementService.ThreadManagementProjectThreadsListError({ projectId, cause: infrastructureCause, }); @@ -260,10 +246,11 @@ it("derives thread management messages from structural error attributes", () => expect(listFailure.message).toBe(`Unable to list threads in project ${projectId}.`); expect(listFailure.message).not.toContain(infrastructureCause.message); - const durableProjectionFailure = new ThreadManagementDurableRunProjectionError({ - threadId, - messageId, - }); + const durableProjectionFailure = + new ThreadManagementService.ThreadManagementDurableRunProjectionError({ + threadId, + messageId, + }); expect(durableProjectionFailure).toMatchObject({ threadId, messageId }); expect(durableProjectionFailure.message).toBe( `Message ${messageId} was accepted on thread ${threadId} without a durable run projection.`, @@ -274,23 +261,23 @@ it.effect("classifies projection infrastructure failures separately from a missi const projectId = ProjectId.make("project:thread-management:projection-failure"); const threadId = ThreadId.make("thread:thread-management:projection-failure"); const infrastructureCause = new Error("sqlite read failed"); - const projectionError = new OrchestratorProjectionError({ + const projectionError = new Orchestrator.OrchestratorProjectionError({ threadId, cause: infrastructureCause, }); - const testLayer = layer.pipe( + const testLayer = ThreadManagementService.layer.pipe( Layer.provide( - Layer.mock(OrchestratorV2)({ + Layer.mock(Orchestrator.OrchestratorV2)({ getThreadProjection: () => Effect.fail(projectionError), }), ), ); return Effect.gen(function* () { - const service = yield* ThreadManagementService; + const service = yield* ThreadManagementService.ThreadManagementService; const error = yield* Effect.flip(service.getProjectThread({ projectId, threadId })); - expect(error).toBeInstanceOf(ThreadManagementProjectionLoadError); + expect(error).toBeInstanceOf(ThreadManagementService.ThreadManagementProjectionLoadError); expect(error).toMatchObject({ projectId, threadId, @@ -311,19 +298,19 @@ it.effect("uses thread-not-found only after a projection loads outside the proje deletedAt: null, }, } as OrchestrationV2ThreadProjection; - const testLayer = layer.pipe( + const testLayer = ThreadManagementService.layer.pipe( Layer.provide( - Layer.mock(OrchestratorV2)({ + Layer.mock(Orchestrator.OrchestratorV2)({ getThreadProjection: () => Effect.succeed(projection), }), ), ); return Effect.gen(function* () { - const service = yield* ThreadManagementService; + const service = yield* ThreadManagementService.ThreadManagementService; const error = yield* Effect.flip(service.getProjectThread({ projectId, threadId })); - expect(error).toBeInstanceOf(ThreadManagementThreadNotFoundError); + expect(error).toBeInstanceOf(ThreadManagementService.ThreadManagementThreadNotFoundError); expect(error).toMatchObject({ projectId, threadId }); expect("cause" in error).toBe(false); }).pipe(Effect.provide(testLayer)); @@ -331,28 +318,28 @@ it.effect("uses thread-not-found only after a projection loads outside the proje it.effect("preserves failed legacy materialization when reading checkpoint context", () => { const threadId = ThreadId.make("thread:thread-management:checkpoint-import-failure"); - const importError = new LegacyV1ThreadImportError({ + const importError = new LegacyV1ThreadImporter.LegacyV1ThreadImportError({ threadId, operation: "hydrate transcript for", cause: new Error("checkpoint import failed"), }); - const testLayer = layerWithLegacyImporter.pipe( + const testLayer = ThreadManagementService.layerWithLegacyImporter.pipe( Layer.provide( Layer.mergeAll( - Layer.mock(OrchestratorV2)({ + Layer.mock(Orchestrator.OrchestratorV2)({ getCheckpointContext: () => Effect.succeed({ runs: [], checkpointScopes: [], checkpoints: [] }), }), - Layer.mock(LegacyV1ThreadImporter)({ + Layer.mock(LegacyV1ThreadImporter.LegacyV1ThreadImporter)({ ensureTranscript: () => Effect.fail(importError), }), ), ), ); return Effect.gen(function* () { - const service = yield* ThreadManagementService; + const service = yield* ThreadManagementService.ThreadManagementService; const error = yield* service.getCheckpointContext(threadId).pipe(Effect.flip); - expect(error).toBeInstanceOf(OrchestratorProjectionError); + expect(error).toBeInstanceOf(Orchestrator.OrchestratorProjectionError); expect(error).toMatchObject({ threadId, cause: importError }); }).pipe(Effect.provide(testLayer)); }); @@ -378,9 +365,9 @@ for (const scenario of [ thread: { id: threadId, projectId, deletedAt: null }, runs: status === "missing" ? [] : [{ id: runId, status }], }) as unknown as OrchestrationV2ThreadProjection; - const testLayer = layer.pipe( + const testLayer = ThreadManagementService.layer.pipe( Layer.provide( - Layer.mock(OrchestratorV2)({ + Layer.mock(Orchestrator.OrchestratorV2)({ getThreadRecords: () => Effect.gen(function* () { reads += 1; @@ -398,7 +385,9 @@ for (const scenario of [ }), ), ); - const service = yield* ThreadManagementService.pipe(Effect.provide(testLayer)); + const service = yield* ThreadManagementService.ThreadManagementService.pipe( + Effect.provide(testLayer), + ); const fiber = yield* service .waitForThread({ projectId, @@ -414,7 +403,7 @@ for (const scenario of [ if (scenario.finalStatus === "missing") { expect(result._tag).toBe("Failure"); expect(result).toMatchObject({ - failure: expect.any(ThreadManagementRunNotFoundError), + failure: expect.any(ThreadManagementService.ThreadManagementRunNotFoundError), }); expect(result).toMatchObject({ failure: { threadId, runId }, diff --git a/apps/server/src/orchestration-v2/ThreadManagementService.ts b/apps/server/src/orchestration-v2/ThreadManagementService.ts index 7a2a9c353dbc..b489f6890967 100644 --- a/apps/server/src/orchestration-v2/ThreadManagementService.ts +++ b/apps/server/src/orchestration-v2/ThreadManagementService.ts @@ -31,13 +31,7 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; -import { - OrchestratorDispatchError, - OrchestratorProjectionError, - OrchestratorV2, - type OrchestratorV2DispatchResult, - type OrchestratorV2Error, -} from "./Orchestrator.ts"; +import * as Orchestrator from "./Orchestrator.ts"; import * as LegacyV1ThreadImporter from "./legacy/LegacyV1ThreadImporter.ts"; export type ThreadManagementSendMode = "auto" | "queue" | "steer" | "restart"; @@ -117,7 +111,7 @@ export interface ThreadManagementSendInput { } export interface ThreadManagementSendResult { - readonly dispatch: OrchestratorV2DispatchResult; + readonly dispatch: Orchestrator.OrchestratorV2DispatchResult; readonly projection: Pick< OrchestrationV2ThreadProjection, "thread" | "runs" | "messages" | "turnItems" @@ -155,7 +149,7 @@ export type ThreadManagementInterruptResult = | { readonly type: "interrupt_requested"; readonly run: OrchestrationV2Run; - readonly dispatch: OrchestratorV2DispatchResult; + readonly dispatch: Orchestrator.OrchestratorV2DispatchResult; } | { readonly type: "no_active_run" } | { @@ -271,7 +265,7 @@ export const ThreadManagementError = Schema.Union([ ]); export type ThreadManagementError = typeof ThreadManagementError.Type; -type ThreadManagementFailure = ThreadManagementError | OrchestratorV2Error; +type ThreadManagementFailure = ThreadManagementError | Orchestrator.OrchestratorV2Error; export interface ThreadManagementServiceShape { readonly ensureLegacyTranscript: ( @@ -279,16 +273,16 @@ export interface ThreadManagementServiceShape { ) => Effect.Effect; readonly dispatch: ( command: OrchestrationV2ServerCommand, - ) => Effect.Effect; - readonly getTimelinePage: OrchestratorV2["Service"]["getTimelinePage"]; - readonly getMessageCount: OrchestratorV2["Service"]["getMessageCount"]; - readonly getThreadRecords: OrchestratorV2["Service"]["getThreadRecords"]; + ) => Effect.Effect; + readonly getTimelinePage: Orchestrator.OrchestratorV2["Service"]["getTimelinePage"]; + readonly getMessageCount: Orchestrator.OrchestratorV2["Service"]["getMessageCount"]; + readonly getThreadRecords: Orchestrator.OrchestratorV2["Service"]["getThreadRecords"]; readonly getThreadProjection: ( threadId: ThreadId, - ) => Effect.Effect; - readonly getCheckpointContext: OrchestratorV2["Service"]["getCheckpointContext"]; - readonly getThreadSnapshot: OrchestratorV2["Service"]["getThreadSnapshot"]; - readonly getThreadSnapshotWindow: OrchestratorV2["Service"]["getThreadSnapshotWindow"]; + ) => Effect.Effect; + readonly getCheckpointContext: Orchestrator.OrchestratorV2["Service"]["getCheckpointContext"]; + readonly getThreadSnapshot: Orchestrator.OrchestratorV2["Service"]["getThreadSnapshot"]; + readonly getThreadSnapshotWindow: Orchestrator.OrchestratorV2["Service"]["getThreadSnapshotWindow"]; readonly getProjectThreadRecords: ( input: { readonly projectId: ProjectId; readonly threadId: ThreadId }, fields: ReadonlyArray, @@ -303,8 +297,8 @@ export interface ThreadManagementServiceShape { }) => Effect.Effect; readonly getShellSnapshot: (options?: { readonly location?: "active" | "archive"; - }) => Effect.Effect; - readonly getThreadShell: OrchestratorV2["Service"]["getThreadShell"]; + }) => Effect.Effect; + readonly getThreadShell: Orchestrator.OrchestratorV2["Service"]["getThreadShell"]; readonly listProjectThreads: (input: { readonly projectId: ProjectId; readonly includeSubagents: boolean; @@ -318,10 +312,10 @@ export interface ThreadManagementServiceShape { readonly interruptThread: ( input: ThreadManagementInterruptInput, ) => Effect.Effect; - readonly getThreadEventSequence: OrchestratorV2["Service"]["getThreadEventSequence"]; - readonly streamStoredEvents: OrchestratorV2["Service"]["streamStoredEvents"]; - readonly streamStoredEventsFrom: OrchestratorV2["Service"]["streamStoredEventsFrom"]; - readonly streamDomainEvents: OrchestratorV2["Service"]["streamDomainEvents"]; + readonly getThreadEventSequence: Orchestrator.OrchestratorV2["Service"]["getThreadEventSequence"]; + readonly streamStoredEvents: Orchestrator.OrchestratorV2["Service"]["streamStoredEvents"]; + readonly streamStoredEventsFrom: Orchestrator.OrchestratorV2["Service"]["streamStoredEventsFrom"]; + readonly streamDomainEvents: Orchestrator.OrchestratorV2["Service"]["streamDomainEvents"]; } export class ThreadManagementService extends Context.Service< @@ -380,7 +374,7 @@ function latestSteerableRun( } const make = Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const legacyImporter = yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter; const ensureLegacyTranscript = Effect.fn( @@ -400,7 +394,7 @@ const make = Effect.gen(function* () { ensureLegacyTranscript(threadId).pipe( Effect.mapError( (cause) => - new OrchestratorProjectionError({ + new Orchestrator.OrchestratorProjectionError({ threadId, cause, }), @@ -417,7 +411,7 @@ const make = Effect.gen(function* () { ).pipe( Effect.mapError( (cause) => - new OrchestratorDispatchError({ + new Orchestrator.OrchestratorDispatchError({ commandId: command.commandId, commandType: command.type, cause, @@ -758,13 +752,11 @@ const legacyV1ThreadImporterNoopLayer = Layer.succeed( }), ); -export const layer: Layer.Layer = Layer.effect( - ThreadManagementService, - make, -).pipe(Layer.provide(legacyV1ThreadImporterNoopLayer)); +export const layer: Layer.Layer = + Layer.effect(ThreadManagementService, make).pipe(Layer.provide(legacyV1ThreadImporterNoopLayer)); export const layerWithLegacyImporter: Layer.Layer< ThreadManagementService, never, - LegacyV1ThreadImporter.LegacyV1ThreadImporter | OrchestratorV2 + LegacyV1ThreadImporter.LegacyV1ThreadImporter | Orchestrator.OrchestratorV2 > = Layer.effect(ThreadManagementService, make); diff --git a/apps/server/src/orchestration-v2/ThreadMessageIntake.test.ts b/apps/server/src/orchestration-v2/ThreadMessageIntake.test.ts index c7d790f6db5d..cd6c3c5f5344 100644 --- a/apps/server/src/orchestration-v2/ThreadMessageIntake.test.ts +++ b/apps/server/src/orchestration-v2/ThreadMessageIntake.test.ts @@ -24,7 +24,7 @@ import { OrchestratorCommandPreviouslyRejectedError, OrchestratorDispatchError, } from "./Orchestrator.ts"; -import { ThreadManagementService } from "./ThreadManagementService.ts"; +import * as ThreadManagementService from "./ThreadManagementService.ts"; import { dispatchCommand } from "./ThreadMessageIntake.ts"; const intakeTestLayer = ServerConfig.layerTest(process.cwd(), { @@ -32,7 +32,7 @@ const intakeTestLayer = ServerConfig.layerTest(process.cwd(), { }).pipe(Layer.provideMerge(NodeServices.layer)); const failingDispatch = (captured: OrchestrationV2ServerCommand[]) => - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ dispatch: (command) => { captured.push(command); return Effect.fail( @@ -350,7 +350,7 @@ it.effect("releases claimed copies when the command was already rejected", () => }, }).pipe( Effect.provide( - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ dispatch: (command) => Effect.fail( new OrchestratorCommandPreviouslyRejectedError({ @@ -420,7 +420,7 @@ it.effect("releases claimed copies when dispatch replays an earlier accepted res }, }).pipe( Effect.provide( - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ dispatch: () => Effect.succeed({ sequence: 1, storedEvents }), }), ), @@ -476,7 +476,7 @@ it.effect("releases claimed copies when the recorded answer has no attachments", }, }).pipe( Effect.provide( - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ dispatch: () => Effect.succeed({ sequence: 1, storedEvents }), }), ), @@ -527,7 +527,7 @@ it.effect("releases claimed copies when the recorded answer was text-only", () = }, }).pipe( Effect.provide( - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ dispatch: () => Effect.succeed({ sequence: 1, storedEvents }), }), ), @@ -585,7 +585,7 @@ it.effect("retains claimed copies referenced by a fresh recorded answer", () => }, }).pipe( Effect.provide( - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ // A fresh commit records exactly the attachments this attempt claimed. dispatch: (command) => Effect.succeed({ @@ -707,7 +707,7 @@ it.effect("a retried response re-claims the preserved pending uploads", () => ); const retry = yield* dispatchCommand(command).pipe( Effect.provide( - Layer.mock(ThreadManagementService)({ + Layer.mock(ThreadManagementService.ThreadManagementService)({ dispatch: (dispatched) => { captured.push(dispatched); return Effect.succeed({ sequence: 1, storedEvents: [] }); diff --git a/apps/server/src/orchestration-v2/ThreadPullRequestService.test.ts b/apps/server/src/orchestration-v2/ThreadPullRequestService.test.ts index 4349176bbf7f..eee01fd22d18 100644 --- a/apps/server/src/orchestration-v2/ThreadPullRequestService.test.ts +++ b/apps/server/src/orchestration-v2/ThreadPullRequestService.test.ts @@ -20,17 +20,13 @@ import * as Queue from "effect/Queue"; import * as Stream from "effect/Stream"; import * as TestClock from "effect/testing/TestClock"; -import { GitManager } from "../git/GitManager.ts"; +import * as GitManager from "../git/GitManager.ts"; import * as ProjectStore from "./ProjectStore.ts"; -import { RepositoryIdentityResolver } from "../project/RepositoryIdentityResolver.ts"; -import { PullRequestService } from "../pullRequest/PullRequestService.ts"; -import { ServerActivation } from "../serverActivation.ts"; -import { OrchestratorV2 } from "./Orchestrator.ts"; -import { - make, - projectWorkspaceMatchesSnapshot, - resolveProjectForPullRequestDiscovery, -} from "./ThreadPullRequestService.ts"; +import * as RepositoryIdentityResolver from "../project/RepositoryIdentityResolver.ts"; +import * as PullRequestService from "../pullRequest/PullRequestService.ts"; +import * as ServerActivation from "../serverActivation.ts"; +import * as Orchestrator from "./Orchestrator.ts"; +import * as ThreadPullRequestService from "./ThreadPullRequestService.ts"; describe("ThreadPullRequestServiceV2 project guard", () => { it.effect("discovers a repository from a project shell without enrichment", () => @@ -46,23 +42,26 @@ describe("ThreadPullRequestServiceV2 project guard", () => { updatedAt: "2026-08-01T00:00:00.000Z", }; let resolvedRoot: string | null = null; - const result = yield* resolveProjectForPullRequestDiscovery(project, { - resolve: (root) => { - resolvedRoot = root; - return Effect.succeed({ - canonicalKey: "github.com/pingdotgg/t3code", - locator: { - source: "git-remote" as const, - remoteName: "origin", - remoteUrl: "git@github.com:pingdotgg/t3code.git", - }, - provider: "github" as const, - displayName: "pingdotgg/t3code", - owner: "pingdotgg", - name: "t3code", - }); + const result = yield* ThreadPullRequestService.resolveProjectForPullRequestDiscovery( + project, + { + resolve: (root) => { + resolvedRoot = root; + return Effect.succeed({ + canonicalKey: "github.com/pingdotgg/t3code", + locator: { + source: "git-remote" as const, + remoteName: "origin", + remoteUrl: "git@github.com:pingdotgg/t3code.git", + }, + provider: "github" as const, + displayName: "pingdotgg/t3code", + owner: "pingdotgg", + name: "t3code", + }); + }, }, - }); + ); expect(resolvedRoot).toBe("/workspace/project"); expect(result.repository).toBe("pingdotgg/t3code"); expect(result.project.repositoryIdentity?.canonicalKey).toBe("github.com/pingdotgg/t3code"); @@ -88,8 +87,10 @@ describe("ThreadPullRequestServiceV2 project guard", () => { return Effect.succeed(null); }, }; - yield* resolveProjectForPullRequestDiscovery(project, resolver); - yield* resolveProjectForPullRequestDiscovery(project, resolver, { refresh: true }); + yield* ThreadPullRequestService.resolveProjectForPullRequestDiscovery(project, resolver); + yield* ThreadPullRequestService.resolveProjectForPullRequestDiscovery(project, resolver, { + refresh: true, + }); expect(refreshes).toEqual([false, true]); }), ); @@ -99,12 +100,17 @@ describe("ThreadPullRequestServiceV2 project guard", () => { workspaceRoot: "/workspace/replaced", } satisfies Pick); - expect(projectWorkspaceMatchesSnapshot(currentProject, "/workspace/original")).toBe(false); + expect( + ThreadPullRequestService.projectWorkspaceMatchesSnapshot( + currentProject, + "/workspace/original", + ), + ).toBe(false); }); it("rejects a pull-request result when the project was deleted before dispatch", () => { expect( - projectWorkspaceMatchesSnapshot( + ThreadPullRequestService.projectWorkspaceMatchesSnapshot( Option.none>(), "/workspace/original", ), @@ -116,7 +122,12 @@ describe("ThreadPullRequestServiceV2 project guard", () => { workspaceRoot: "/workspace/original", } satisfies Pick); - expect(projectWorkspaceMatchesSnapshot(currentProject, "/workspace/original")).toBe(true); + expect( + ThreadPullRequestService.projectWorkspaceMatchesSnapshot( + currentProject, + "/workspace/original", + ), + ).toBe(true); }); }); @@ -170,7 +181,7 @@ describe("ThreadPullRequestServiceV2 reads", () => { ThreadId | { readonly location?: string; readonly unsettledOnly?: boolean } >(); const dependencies = Layer.mergeAll( - Layer.mock(OrchestratorV2)({ + Layer.mock(Orchestrator.OrchestratorV2)({ streamDomainEvents: Stream.fromPubSub(events), getShellSnapshot: (options) => Queue.offer(reads, options ?? {}).pipe( @@ -190,10 +201,10 @@ describe("ThreadPullRequestServiceV2 reads", () => { Layer.mock(ProjectStore.ProjectStoreV2)({ listShells: () => Effect.succeed([]), }), - Layer.mock(GitManager)({}), - Layer.mock(PullRequestService)({}), - Layer.mock(RepositoryIdentityResolver)({}), - Layer.succeed(ServerActivation, Deferred.await(activation)), + Layer.mock(GitManager.GitManager)({}), + Layer.mock(PullRequestService.PullRequestService)({}), + Layer.mock(RepositoryIdentityResolver.RepositoryIdentityResolver)({}), + Layer.succeed(ServerActivation.ServerActivation, Deferred.await(activation)), Layer.succeed( Crypto.Crypto, Crypto.make({ @@ -205,7 +216,7 @@ describe("ThreadPullRequestServiceV2 reads", () => { ); yield* Effect.gen(function* () { - const service = yield* make; + const service = yield* ThreadPullRequestService.make; yield* service.start(); yield* Deferred.succeed(activation, undefined); // Startup backfill reads every active thread. @@ -263,7 +274,7 @@ describe("ThreadPullRequestServiceV2 reads", () => { readonly unsettledOnly?: boolean; }>(); const dependencies = Layer.mergeAll( - Layer.mock(OrchestratorV2)({ + Layer.mock(Orchestrator.OrchestratorV2)({ streamDomainEvents: Stream.never, getShellSnapshot: (options) => Queue.offer(reads, options ?? {}).pipe( @@ -279,10 +290,10 @@ describe("ThreadPullRequestServiceV2 reads", () => { Layer.mock(ProjectStore.ProjectStoreV2)({ listShells: () => Effect.succeed([]), }), - Layer.mock(GitManager)({}), - Layer.mock(PullRequestService)({}), - Layer.mock(RepositoryIdentityResolver)({}), - Layer.succeed(ServerActivation, Deferred.await(activation)), + Layer.mock(GitManager.GitManager)({}), + Layer.mock(PullRequestService.PullRequestService)({}), + Layer.mock(RepositoryIdentityResolver.RepositoryIdentityResolver)({}), + Layer.succeed(ServerActivation.ServerActivation, Deferred.await(activation)), Layer.succeed( Crypto.Crypto, Crypto.make({ @@ -294,7 +305,7 @@ describe("ThreadPullRequestServiceV2 reads", () => { ); yield* Effect.gen(function* () { - const service = yield* make; + const service = yield* ThreadPullRequestService.make; yield* service.start(); yield* Deferred.succeed(activation, undefined); // Backfill finds the settled branch thread and must read it again. diff --git a/apps/server/src/orchestration-v2/ThreadPullRequestService.ts b/apps/server/src/orchestration-v2/ThreadPullRequestService.ts index 0e4203aacf7a..fff3688fddc7 100644 --- a/apps/server/src/orchestration-v2/ThreadPullRequestService.ts +++ b/apps/server/src/orchestration-v2/ThreadPullRequestService.ts @@ -27,7 +27,7 @@ import * as ProjectStore from "./ProjectStore.ts"; import * as PullRequestService from "../pullRequest/PullRequestService.ts"; import * as RepositoryIdentityResolver from "../project/RepositoryIdentityResolver.ts"; import { forkParked } from "../serverActivation.ts"; -import { OrchestratorV2 } from "./Orchestrator.ts"; +import * as Orchestrator from "./Orchestrator.ts"; class ThreadPullRequestServiceV2 extends Context.Service< ThreadPullRequestServiceV2, @@ -98,7 +98,7 @@ interface RefreshRequest { } export const make = Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const projectStore = yield* ProjectStore.ProjectStoreV2; const git = yield* GitManager.GitManager; const pullRequests = yield* PullRequestService.PullRequestService; diff --git a/apps/server/src/orchestration-v2/ThreadSettlementService.test.ts b/apps/server/src/orchestration-v2/ThreadSettlementService.test.ts index 1f598d86136c..03933d9d0048 100644 --- a/apps/server/src/orchestration-v2/ThreadSettlementService.test.ts +++ b/apps/server/src/orchestration-v2/ThreadSettlementService.test.ts @@ -14,7 +14,7 @@ import { type OrchestrationV2ShellSnapshot, type OrchestrationV2ThreadShell, type PullRequestSummary, - type ServerSettings, + type ServerSettings as ContractServerSettings, type ServerSettingsPatch, } from "@t3tools/contracts"; import { applyServerSettingsPatch } from "@t3tools/shared/serverSettings"; @@ -29,27 +29,16 @@ import * as Ref from "effect/Ref"; import * as Stream from "effect/Stream"; import { TestClock } from "effect/testing"; -import { GitManager, type GitBranchPullRequest } from "../git/GitManager.ts"; -import { - PullRequestService, - type PullRequestMergeEvent, -} from "../pullRequest/PullRequestService.ts"; -import { ServerActivation } from "../serverActivation.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; -import { TerminalManager } from "../terminal/Manager.ts"; +import * as GitManager from "../git/GitManager.ts"; +import * as PullRequestService from "../pullRequest/PullRequestService.ts"; +import * as ServerActivation from "../serverActivation.ts"; +import * as ServerSettings from "../serverSettings.ts"; +import * as TerminalManager from "../terminal/Manager.ts"; import * as ProjectStore from "./ProjectStore.ts"; -import { OrchestratorV2, type OrchestratorV2Shape } from "./Orchestrator.ts"; -import { ProjectionStoreV2 } from "./ProjectionStore.ts"; +import * as Orchestrator from "./Orchestrator.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; import * as ThreadSettlementService from "./ThreadSettlementService.ts"; -import { - autoSettlementSettingsKey, - isAutoSettlementCandidate, - QUEUED_TURN_START_GRACE_MS, - resolveAutoSettlementAt, - threadHasQueuedTurnStart, -} from "./ThreadSettlementService.ts"; - const NOW_MS = Date.parse("2026-06-10T12:00:00.000Z"); const DAY_MS = 24 * 60 * 60 * 1_000; @@ -108,15 +97,39 @@ function shell(overrides: Partial = {}): Orchestrati describe("isAutoSettlementCandidate", () => { it("excludes overridden, pinned, blocked, and working threads", () => { - expect(isAutoSettlementCandidate(shell(), NOW_MS)).toBe(true); - expect(isAutoSettlementCandidate(shell({ archivedAt: at(-1) }), NOW_MS)).toBe(false); - expect(isAutoSettlementCandidate(shell({ settledOverride: "settled" }), NOW_MS)).toBe(false); - expect(isAutoSettlementCandidate(shell({ settledOverride: "active" }), NOW_MS)).toBe(false); - expect(isAutoSettlementCandidate(shell({ pinnedAt: at(-1) }), NOW_MS)).toBe(false); - expect(isAutoSettlementCandidate(shell({ autoSettleDisabledAt: at(-1) }), NOW_MS)).toBe(false); - expect(isAutoSettlementCandidate(shell({ activityRunStatus: "running" }), NOW_MS)).toBe(false); + expect(ThreadSettlementService.isAutoSettlementCandidate(shell(), NOW_MS)).toBe(true); + expect( + ThreadSettlementService.isAutoSettlementCandidate(shell({ archivedAt: at(-1) }), NOW_MS), + ).toBe(false); + expect( + ThreadSettlementService.isAutoSettlementCandidate( + shell({ settledOverride: "settled" }), + NOW_MS, + ), + ).toBe(false); + expect( + ThreadSettlementService.isAutoSettlementCandidate( + shell({ settledOverride: "active" }), + NOW_MS, + ), + ).toBe(false); + expect( + ThreadSettlementService.isAutoSettlementCandidate(shell({ pinnedAt: at(-1) }), NOW_MS), + ).toBe(false); + expect( + ThreadSettlementService.isAutoSettlementCandidate( + shell({ autoSettleDisabledAt: at(-1) }), + NOW_MS, + ), + ).toBe(false); expect( - isAutoSettlementCandidate( + ThreadSettlementService.isAutoSettlementCandidate( + shell({ activityRunStatus: "running" }), + NOW_MS, + ), + ).toBe(false); + expect( + ThreadSettlementService.isAutoSettlementCandidate( shell({ pendingRuntimeRequest: { kind: "approval" } as never, }), @@ -124,7 +137,7 @@ describe("isAutoSettlementCandidate", () => { ), ).toBe(false); expect( - isAutoSettlementCandidate( + ThreadSettlementService.isAutoSettlementCandidate( shell({ pendingBackgroundTasks: [{ label: "task" }] as never }), NOW_MS, ), @@ -136,51 +149,54 @@ describe("isAutoSettlementCandidate", () => { snoozedUntil: at(60 * 60 * 1_000), snoozedAt: at(-60 * 60 * 1_000), }); - expect(isAutoSettlementCandidate(snoozed, NOW_MS)).toBe(false); + expect(ThreadSettlementService.isAutoSettlementCandidate(snoozed, NOW_MS)).toBe(false); expect( - isAutoSettlementCandidate( + ThreadSettlementService.isAutoSettlementCandidate( shell({ ...snoozed, status: "failed", latestRunCompletedAt: at(-30 * 60 * 1_000) }), NOW_MS, ), ).toBe(true); expect( - isAutoSettlementCandidate( + ThreadSettlementService.isAutoSettlementCandidate( shell({ ...snoozed, latestRunCompletedAt: at(-30 * 60 * 1_000) }), NOW_MS, ), ).toBe(true); expect( - isAutoSettlementCandidate( + ThreadSettlementService.isAutoSettlementCandidate( shell({ ...snoozed, status: "failed", latestRunCompletedAt: at(-2 * 60 * 60 * 1_000) }), NOW_MS, ), ).toBe(false); expect( - isAutoSettlementCandidate( + ThreadSettlementService.isAutoSettlementCandidate( shell({ ...snoozed, status: "failed", latestRunCompletedAt: snoozed.snoozedAt }), NOW_MS, ), ).toBe(false); expect( - isAutoSettlementCandidate( + ThreadSettlementService.isAutoSettlementCandidate( shell({ ...snoozed, status: "failed", latestRunCompletedAt: null }), NOW_MS, ), ).toBe(false); // Expired snooze is no longer a park. - expect(isAutoSettlementCandidate(shell({ ...snoozed, snoozedUntil: at(-1) }), NOW_MS)).toBe( - true, - ); + expect( + ThreadSettlementService.isAutoSettlementCandidate( + shell({ ...snoozed, snoozedUntil: at(-1) }), + NOW_MS, + ), + ).toBe(true); }); }); describe("threadHasQueuedTurnStart", () => { it("holds a fresh unadopted user message inside the grace window only", () => { const fresh = shell({ latestUserMessageAt: at(-1_000) }); - expect(threadHasQueuedTurnStart(fresh, NOW_MS)).toBe(true); + expect(ThreadSettlementService.threadHasQueuedTurnStart(fresh, NOW_MS)).toBe(true); // Adoption stamps the run with the message time, clearing the hold. expect( - threadHasQueuedTurnStart( + ThreadSettlementService.threadHasQueuedTurnStart( shell({ latestUserMessageAt: at(-1_000), latestRunId: "run-1" as never, @@ -191,21 +207,21 @@ describe("threadHasQueuedTurnStart", () => { ).toBe(false); // Outside the grace window the stale message no longer blocks. expect( - threadHasQueuedTurnStart( - shell({ latestUserMessageAt: at(-QUEUED_TURN_START_GRACE_MS - 1) }), + ThreadSettlementService.threadHasQueuedTurnStart( + shell({ latestUserMessageAt: at(-ThreadSettlementService.QUEUED_TURN_START_GRACE_MS - 1) }), NOW_MS, ), ).toBe(false); // Client clocks ahead of the server must not extend the hold. expect( - threadHasQueuedTurnStart( - shell({ latestUserMessageAt: at(QUEUED_TURN_START_GRACE_MS + 1) }), + ThreadSettlementService.threadHasQueuedTurnStart( + shell({ latestUserMessageAt: at(ThreadSettlementService.QUEUED_TURN_START_GRACE_MS + 1) }), NOW_MS, ), ).toBe(false); // A failed start clears the hold immediately. expect( - threadHasQueuedTurnStart( + ThreadSettlementService.threadHasQueuedTurnStart( shell({ latestUserMessageAt: at(-1_000), status: "failed" }), NOW_MS, ), @@ -228,10 +244,16 @@ describe("resolveAutoSettlementAt", () => { autoSettleAfterDays: 2, autoSettleOnMerge: true, }; - expect(resolveAutoSettlementAt(input)).toEqual(at(-3 * DAY_MS)); - expect(resolveAutoSettlementAt({ ...input, autoSettleAfterDays: 5 })).toBeNull(); - expect(resolveAutoSettlementAt({ ...input, autoSettleAfterDays: null })).toBeNull(); - expect(resolveAutoSettlementAt({ ...input, thread: shell() })).toBeNull(); + expect(ThreadSettlementService.resolveAutoSettlementAt(input)).toEqual(at(-3 * DAY_MS)); + expect( + ThreadSettlementService.resolveAutoSettlementAt({ ...input, autoSettleAfterDays: 5 }), + ).toBeNull(); + expect( + ThreadSettlementService.resolveAutoSettlementAt({ ...input, autoSettleAfterDays: null }), + ).toBeNull(); + expect( + ThreadSettlementService.resolveAutoSettlementAt({ ...input, thread: shell() }), + ).toBeNull(); }); it("settles on merge only after the user's last action and preserves the activity time", () => { @@ -249,17 +271,19 @@ describe("resolveAutoSettlementAt", () => { autoSettleAfterDays: null, autoSettleOnMerge: true, }; - expect(resolveAutoSettlementAt(input)).toEqual(at(-90 * 60 * 1_000)); - expect(resolveAutoSettlementAt({ ...input, autoSettleOnMerge: false })).toBeNull(); + expect(ThreadSettlementService.resolveAutoSettlementAt(input)).toEqual(at(-90 * 60 * 1_000)); expect( - resolveAutoSettlementAt({ + ThreadSettlementService.resolveAutoSettlementAt({ ...input, autoSettleOnMerge: false }), + ).toBeNull(); + expect( + ThreadSettlementService.resolveAutoSettlementAt({ ...input, pullRequest: { state: "merged", mergedAt: DateTime.formatIso(at(-3 * 60 * 60 * 1_000)) }, }), ).toBeNull(); // A thread without messages still has a stable timestamp when its PR closes. expect( - resolveAutoSettlementAt({ + ThreadSettlementService.resolveAutoSettlementAt({ ...input, thread: shell(), pullRequest: { state: "closed", closedAt: DateTime.formatIso(at(-1)) }, @@ -276,10 +300,12 @@ describe("resolveAutoSettlementAt", () => { autoSettleAfterDays: 2, autoSettleOnMerge: true, }; - expect(resolveAutoSettlementAt(input)).toEqual(at(-30 * DAY_MS)); - expect(resolveAutoSettlementAt({ ...input, autoSettleAfterDays: null })).toBeNull(); + expect(ThreadSettlementService.resolveAutoSettlementAt(input)).toEqual(at(-30 * DAY_MS)); + expect( + ThreadSettlementService.resolveAutoSettlementAt({ ...input, autoSettleAfterDays: null }), + ).toBeNull(); expect( - resolveAutoSettlementAt({ + ThreadSettlementService.resolveAutoSettlementAt({ ...input, thread: shell({ latestUserMessageAt: at(-DAY_MS) }), }), @@ -293,11 +319,11 @@ const LINKED_PROJECT_ID = ProjectId.make("linked-settlement-project"); describe("autoSettlementSettingsKey", () => { it("distinguishes a project that inherits the threshold from one that disables it", () => { - const inherits = autoSettlementSettingsKey({ + const inherits = ThreadSettlementService.autoSettlementSettingsKey({ ...DEFAULT_SERVER_SETTINGS, projectSettingsOverrides: { [PROJECT_ID]: { sidebarAutoSettleOnMerge: true } }, }); - const never = autoSettlementSettingsKey({ + const never = ThreadSettlementService.autoSettlementSettingsKey({ ...DEFAULT_SERVER_SETTINGS, projectSettingsOverrides: { [PROJECT_ID]: { sidebarAutoSettleOnMerge: true, sidebarAutoSettleAfterDays: null }, @@ -307,11 +333,11 @@ describe("autoSettlementSettingsKey", () => { }); it("ignores project overrides that do not touch settlement", () => { - const base = autoSettlementSettingsKey({ + const base = ThreadSettlementService.autoSettlementSettingsKey({ ...DEFAULT_SERVER_SETTINGS, projectSettingsOverrides: { [PROJECT_ID]: { sidebarAutoSettleOnMerge: false } }, }); - const unrelated = autoSettlementSettingsKey({ + const unrelated = ThreadSettlementService.autoSettlementSettingsKey({ ...DEFAULT_SERVER_SETTINGS, projectSettingsOverrides: { [LINKED_PROJECT_ID]: { defaultThreadEnvMode: "worktree" }, @@ -405,14 +431,14 @@ function makeBranchPullRequest(state: "open" | "closed" | "merged") { updatedAt: NOW, closedAt: state === "closed" ? NOW : null, mergedAt: state === "merged" ? NOW : null, - } satisfies GitBranchPullRequest; + } satisfies GitManager.GitBranchPullRequest; } interface HarnessOptions { readonly snapshot: OrchestrationV2ShellSnapshot; - readonly settings?: ServerSettings; - readonly branchPullRequest?: GitManager["Service"]["branchPullRequest"]; - readonly pullRequestSummary?: PullRequestService["Service"]["summary"]; + readonly settings?: ContractServerSettings; + readonly branchPullRequest?: GitManager.GitManager["Service"]["branchPullRequest"]; + readonly pullRequestSummary?: PullRequestService.PullRequestService["Service"]["summary"]; readonly existingWorktreePaths?: ReadonlyArray; readonly onDispatch?: (command: AutoSettleCommand) => Effect.Effect; /** Threads `getThread` returns when a `thread.settled` event is handled. */ @@ -426,8 +452,8 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options: const snapshotReads = yield* Queue.unbounded(); const candidateReads = yield* Ref.make>([]); const settings = yield* Ref.make(options.settings ?? DEFAULT_SERVER_SETTINGS); - const settingsChanges = yield* PubSub.unbounded(); - const mergedPullRequests = yield* PubSub.unbounded(); + const settingsChanges = yield* PubSub.unbounded(); + const mergedPullRequests = yield* PubSub.unbounded(); const commands = yield* Ref.make>([]); const branchCalls = yield* Ref.make< ReadonlyArray<{ readonly cwd: string; readonly branch: string }> @@ -452,11 +478,14 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options: return next; }); - const branchPullRequest: GitManager["Service"]["branchPullRequest"] = (input) => + const branchPullRequest: GitManager.GitManager["Service"]["branchPullRequest"] = (input) => Ref.update(branchCalls, (calls) => [...calls, input]).pipe( Effect.andThen(options.branchPullRequest?.(input) ?? Effect.succeed(null)), ); - const pullRequestSummary: PullRequestService["Service"]["summary"] = (input, readOptions) => + const pullRequestSummary: PullRequestService.PullRequestService["Service"]["summary"] = ( + input, + readOptions, + ) => Effect.gen(function* () { yield* Ref.update(summaryCalls, (calls) => [...calls, input]); yield* Ref.update(summaryRecovery, (values) => [ @@ -474,7 +503,7 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options: ); }); - const dispatch: OrchestratorV2Shape["dispatch"] = (command) => { + const dispatch: Orchestrator.OrchestratorV2Shape["dispatch"] = (command) => { if (command.type !== "thread.auto-settle") { return Effect.die(new Error(`Unexpected command: ${command.type}`)); } @@ -484,7 +513,7 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options: ); }; - const serverSettings = ServerSettingsService.of({ + const serverSettings = ServerSettings.ServerSettingsService.of({ start: Effect.void, ready: Effect.void, getSettings: Ref.get(settings), @@ -501,7 +530,7 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options: Layer.mock(ProjectStore.ProjectStoreV2)({ listShells: () => Ref.get(snapshots).pipe(Effect.map((snapshot) => snapshot.projects)), }), - Layer.mock(ProjectionStoreV2)({ + Layer.mock(ProjectionStore.ProjectionStoreV2)({ getSettlementCandidates: (threadId) => Ref.update(candidateReads, (reads) => [...reads, threadId]).pipe( Effect.andThen(Ref.updateAndGet(snapshotReadCount, (count) => count + 1)), @@ -520,25 +549,25 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options: : Effect.die(new Error(`Unexpected thread read: ${threadId}`)); }, }), - Layer.mock(OrchestratorV2)({ + Layer.mock(Orchestrator.OrchestratorV2)({ streamDomainEvents: Stream.fromPubSub(domainEvents), dispatch, }), - Layer.mock(TerminalManager)({ + Layer.mock(TerminalManager.TerminalManager)({ closeIdle: (input) => Queue.offer(closedIdle, input).pipe(Effect.asVoid), }), - Layer.mock(GitManager)({ + Layer.mock(GitManager.GitManager)({ branchPullRequest, invalidateStatus: (cwd) => Ref.update(invalidatedCwds, (cwds) => [...cwds, cwd]), }), - Layer.mock(PullRequestService)({ + Layer.mock(PullRequestService.PullRequestService)({ summary: pullRequestSummary, subscribeMerges: PubSub.subscribe(mergedPullRequests).pipe( Effect.map((subscription) => Stream.fromSubscription(subscription)), ), }), - Layer.succeed(ServerSettingsService, serverSettings), - Layer.succeed(ServerActivation, Deferred.await(activation)), + Layer.succeed(ServerSettings.ServerSettingsService, serverSettings), + Layer.succeed(ServerActivation.ServerActivation, Deferred.await(activation)), Layer.succeed(Crypto.Crypto, testCrypto), FileSystem.layerNoop({ exists: (path) => Effect.succeed(options.existingWorktreePaths?.includes(path) ?? false), diff --git a/apps/server/src/orchestration-v2/ThreadSettlementService.ts b/apps/server/src/orchestration-v2/ThreadSettlementService.ts index 65584acf133d..74c9de8d6b37 100644 --- a/apps/server/src/orchestration-v2/ThreadSettlementService.ts +++ b/apps/server/src/orchestration-v2/ThreadSettlementService.ts @@ -24,8 +24,8 @@ import * as ServerSettings from "../serverSettings.ts"; import { forkParked } from "../serverActivation.ts"; import * as TerminalManager from "../terminal/Manager.ts"; import * as ProjectStore from "./ProjectStore.ts"; -import { OrchestratorV2 } from "./Orchestrator.ts"; -import { ProjectionStoreV2, type ProjectionSettlementCandidate } from "./ProjectionStore.ts"; +import * as Orchestrator from "./Orchestrator.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; export interface SettlementPullRequest { readonly state: "open" | "closed" | "merged"; @@ -131,7 +131,7 @@ function pullRequestSettles( /** Cheap checks that run before any source control lookup. */ export function isAutoSettlementCandidate( - thread: ProjectionSettlementCandidate, + thread: ProjectionStore.ProjectionSettlementCandidate, nowMs: number, ): boolean { if (thread.archivedAt !== null || thread.settledOverride !== null) return false; @@ -157,7 +157,7 @@ export function isAutoSettlementCandidate( } export function resolveAutoSettlementAt(input: { - readonly thread: ProjectionSettlementCandidate; + readonly thread: ProjectionStore.ProjectionSettlementCandidate; readonly pullRequest: SettlementPullRequest | null; readonly nowMs: number; readonly autoSettleAfterDays: number | null; @@ -250,8 +250,8 @@ export function autoSettlementSettingsKey( } export const make = Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const projections = yield* ProjectionStoreV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; const projectStore = yield* ProjectStore.ProjectStoreV2; const settingsService = yield* ServerSettings.ServerSettingsService; const git = yield* GitManager.GitManager; diff --git a/apps/server/src/orchestration-v2/ThreadTitleRegenerationService.test.ts b/apps/server/src/orchestration-v2/ThreadTitleRegenerationService.test.ts index 0f2e9cb1313e..6b9d4a43cc27 100644 --- a/apps/server/src/orchestration-v2/ThreadTitleRegenerationService.test.ts +++ b/apps/server/src/orchestration-v2/ThreadTitleRegenerationService.test.ts @@ -26,7 +26,6 @@ import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; import * as ThreadManagement from "./ThreadManagementService.ts"; import * as ThreadTitleRegeneration from "./ThreadTitleRegenerationService.ts"; -import { formatThreadTitleContext } from "./ThreadTitleRegenerationService.ts"; import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; const projectId = ProjectId.make("project:title-regeneration"); @@ -165,7 +164,7 @@ describe("formatThreadTitleContext", () => { }); it("builds a newest-first digest, skipping system messages and empty sections", () => { - const context = formatThreadTitleContext([ + const context = ThreadTitleRegeneration.formatThreadTitleContext([ { role: "user", text: "First question" }, { role: "system", text: "Hidden instructions" }, { role: "assistant", text: "" }, @@ -182,7 +181,7 @@ describe("formatThreadTitleContext", () => { }); it("pins the first user message ahead of the retained tail once content stops fitting", () => { - const context = formatThreadTitleContext([ + const context = ThreadTitleRegeneration.formatThreadTitleContext([ { role: "user", text: `Ancient context that anchors the topic ${"x".repeat(600)}` }, { role: "assistant", text: "y".repeat(6_000) }, { role: "user", text: "z".repeat(1_500) }, @@ -193,7 +192,7 @@ describe("formatThreadTitleContext", () => { }); it("truncates an oversized pinned first user message", () => { - const context = formatThreadTitleContext([ + const context = ThreadTitleRegeneration.formatThreadTitleContext([ { role: "user", text: `Topic anchor ${"a".repeat(4_000)}` }, { role: "assistant", text: "y".repeat(9_000) }, { role: "user", text: "z".repeat(1_500) }, @@ -204,7 +203,7 @@ describe("formatThreadTitleContext", () => { }); it("retains at most four attachments from the newest messages", () => { - const context = formatThreadTitleContext([ + const context = ThreadTitleRegeneration.formatThreadTitleContext([ { role: "user", text: "older", attachments: [attachment("a"), attachment("b")] }, { role: "user", diff --git a/apps/server/src/orchestration-v2/ThreadTitleRegenerationService.ts b/apps/server/src/orchestration-v2/ThreadTitleRegenerationService.ts index dff2cc53f520..4d3961019314 100644 --- a/apps/server/src/orchestration-v2/ThreadTitleRegenerationService.ts +++ b/apps/server/src/orchestration-v2/ThreadTitleRegenerationService.ts @@ -17,7 +17,7 @@ import * as ServerSettings from "../serverSettings.ts"; import * as TextGeneration from "../textGeneration/TextGeneration.ts"; import type { OrchestratorV2Error } from "./Orchestrator.ts"; import * as ProjectStore from "./ProjectStore.ts"; -import { ThreadManagementService } from "./ThreadManagementService.ts"; +import * as ThreadManagementService from "./ThreadManagementService.ts"; import { formatThreadTitleContext } from "../textGeneration/ThreadTitleContext.ts"; export { formatThreadTitleContext } from "../textGeneration/ThreadTitleContext.ts"; @@ -39,7 +39,7 @@ export class ThreadTitleRegenerationService extends Context.Service< >()("t3/orchestration-v2/ThreadTitleRegenerationService") {} const make = Effect.gen(function* () { - const threads = yield* ThreadManagementService; + const threads = yield* ThreadManagementService.ThreadManagementService; const projects = yield* ProjectStore.ProjectStoreV2; const serverSettings = yield* ServerSettings.ServerSettingsService; const textGeneration = yield* TextGeneration.TextGeneration; diff --git a/apps/server/src/orchestration-v2/TurnStartReads.test.ts b/apps/server/src/orchestration-v2/TurnStartReads.test.ts index 5f990e9d9976..7c24750e7ed6 100644 --- a/apps/server/src/orchestration-v2/TurnStartReads.test.ts +++ b/apps/server/src/orchestration-v2/TurnStartReads.test.ts @@ -17,23 +17,18 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; -import { - ProjectionStoreV2, - ProjectionStoreThreadNotFoundError, - layer, - layerMemory, -} from "./ProjectionStore.ts"; +import * as ProjectionStore from "./ProjectionStore.ts"; const databaseLayer = Layer.mergeAll( SqlitePersistenceMemory, - layer.pipe(Layer.provide(SqlitePersistenceMemory)), + ProjectionStore.layer.pipe(Layer.provide(SqlitePersistenceMemory)), ); it.effect.each(["sqlite", "memory"] as const)( "loads startup state without obsolete transcript payloads in %s", (storage) => Effect.gen(function* () { - const store = yield* ProjectionStoreV2; + const store = yield* ProjectionStore.ProjectionStoreV2; const now = yield* DateTime.now; const threadId = ThreadId.make("thread:completion-reads"); const runId = RunId.make("run:completion-reads"); @@ -362,11 +357,13 @@ it.effect.each(["sqlite", "memory"] as const)( assert.isFalse((yield* store.getTurnStartContext(other, runId)).hasConversation); assert.instanceOf( yield* store.getTurnStartContext(ThreadId.make("missing"), runId).pipe(Effect.flip), - ProjectionStoreThreadNotFoundError, + ProjectionStore.ProjectionStoreThreadNotFoundError, ); }).pipe( Effect.provide( - storage === "sqlite" ? databaseLayer : Layer.merge(SqlitePersistenceMemory, layerMemory), + storage === "sqlite" + ? databaseLayer + : Layer.merge(SqlitePersistenceMemory, ProjectionStore.layerMemory), ), ), ); diff --git a/apps/server/src/orchestration-v2/runtimeLayer.test.ts b/apps/server/src/orchestration-v2/runtimeLayer.test.ts index 60cad80722e9..c71f84709b27 100644 --- a/apps/server/src/orchestration-v2/runtimeLayer.test.ts +++ b/apps/server/src/orchestration-v2/runtimeLayer.test.ts @@ -1,5 +1,5 @@ import { limitRecoveryCommand } from "./UsageLimitRecoveryWorker.ts"; -import { SourceControlProviderRegistry } from "../sourceControl/SourceControlProviderRegistry.ts"; +import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it } from "@effect/vitest"; import { vi } from "vite-plus/test"; @@ -38,36 +38,29 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; import * as GitWorkflow from "../git/GitWorkflowService.ts"; -import { ServerConfig } from "../config.ts"; +import * as ServerConfig from "../config.ts"; import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; -import { OrchestrationEventStore } from "../persistence/Services/OrchestrationEventStore.ts"; -import { ProjectEnrichmentService } from "../project/ProjectEnrichmentService.ts"; +import * as OrchestrationEventStore from "../persistence/Services/OrchestrationEventStore.ts"; +import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; import * as ProjectService from "../project/ProjectService.ts"; -import { ServerSettingsService } from "../serverSettings.ts"; -import { layer as mcpSessionRegistryTestLayer } from "../mcp/McpSessionRegistry.testkit.ts"; -import { ProviderInstanceRegistry } from "../provider/Services/ProviderInstanceRegistry.ts"; +import * as ServerSettings from "../serverSettings.ts"; +import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; +import * as ProviderInstanceRegistry from "../provider/Services/ProviderInstanceRegistry.ts"; import type { ProviderInstance } from "../provider/ProviderDriver.ts"; import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; import * as VcsProcess from "../vcs/VcsProcess.ts"; -import { WorkspacePaths } from "../workspace/WorkspacePaths.ts"; -import { - LegacyV1ThreadImporter, - LegacyV1ThreadImportError, -} from "./legacy/LegacyV1ThreadImporter.ts"; -import { - OrchestratorDispatchError, - OrchestratorProjectionError, - OrchestratorV2, -} from "./Orchestrator.ts"; +import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; +import * as LegacyV1ThreadImporter from "./legacy/LegacyV1ThreadImporter.ts"; +import * as Orchestrator from "./Orchestrator.ts"; import { ROLLBACK_FAILED_MESSAGE } from "./CheckpointRollbackService.ts"; -import { OrchestrationEffectWorkerV2 } from "./EffectWorker.ts"; -import { EffectOutboxV2, layer as effectOutboxLayer } from "./EffectOutbox.ts"; -import { EventSinkV2 } from "./EventSink.ts"; -import { ProviderRuntimeRecoveryService } from "./ProviderRuntimeRecoveryService.ts"; -import { ProjectionMaintenanceV2 } from "./ProjectionMaintenance.ts"; +import * as EffectWorker from "./EffectWorker.ts"; +import * as EffectOutbox from "./EffectOutbox.ts"; +import * as EventSink from "./EventSink.ts"; +import * as ProviderRuntimeRecoveryService from "./ProviderRuntimeRecoveryService.ts"; +import * as ProjectionMaintenance from "./ProjectionMaintenance.ts"; import * as ProjectStore from "./ProjectStore.ts"; import type { ProviderAdapterV2SessionRuntime, ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; -import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts"; +import * as ProviderSessionManager from "./ProviderSessionManager.ts"; import { OrchestrationEventInfrastructureLayerLive, OrchestrationV2EventSinkLayerLive, @@ -76,15 +69,14 @@ import { } from "./runtimeLayer.ts"; import { shellStreamItemFromThreadShell } from "./ShellStream.ts"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; -import { ThreadManagementService } from "./ThreadManagementService.ts"; -import { - ThreadCommandExecutor, - layer as threadCommandExecutorLayer, -} from "./ThreadCommandExecutor.ts"; +import * as ThreadManagementService from "./ThreadManagementService.ts"; +import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts"; const PlatformTestLayer = Layer.merge( NodeServices.layer, - Layer.mock(SourceControlProviderRegistry)({ resolveLink: () => Effect.die("unused title link") }), + Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ + resolveLink: () => Effect.die("unused title link"), + }), ); const ServerConfigLayer = ServerConfig.layerTest(process.cwd(), { @@ -150,18 +142,21 @@ const alternateProviderInstance = { }, } satisfies ProviderInstance; -const TestProviderInstanceRegistry = Layer.succeed(ProviderInstanceRegistry, { - getInstance: (instanceId) => - Effect.succeed( - [providerInstance, alternateProviderInstance].find( - (instance) => instanceId === instance.instanceId, +const TestProviderInstanceRegistry = Layer.succeed( + ProviderInstanceRegistry.ProviderInstanceRegistry, + { + getInstance: (instanceId) => + Effect.succeed( + [providerInstance, alternateProviderInstance].find( + (instance) => instanceId === instance.instanceId, + ), ), - ), - listInstances: Effect.succeed([providerInstance, alternateProviderInstance]), - listUnavailable: Effect.succeed([]), - streamChanges: Stream.empty, - subscribeChanges: Effect.never, -}); + listInstances: Effect.succeed([providerInstance, alternateProviderInstance]), + listUnavailable: Effect.succeed([]), + streamChanges: Stream.empty, + subscribeChanges: Effect.never, + }, +); /** Seed a project row the way a committed `project.created` event folds into it. */ const seedProject = (input: { @@ -217,14 +212,14 @@ const TestLayer = Layer.mergeAll( OrchestrationV2LayerLive, OrchestrationV2EventSinkLayerLive, ProjectStore.layer, - effectOutboxLayer, - threadCommandExecutorLayer, + EffectOutbox.layer, + ThreadCommandExecutor.layer, ).pipe( - Layer.provide(mcpSessionRegistryTestLayer), + Layer.provide(McpSessionRegistryTestkit.layer), Layer.provide(SqlitePersistenceMemory), Layer.provide(CheckpointStoreTestLayer), Layer.provide(ServerConfigLayer), - Layer.provide(ServerSettingsService.layerTest()), + Layer.provide(ServerSettings.layerTest()), Layer.provide(TestProviderInstanceRegistry), Layer.provide(GitWorkflowTestLayer), Layer.provide(ProjectServiceTestLayer), @@ -232,11 +227,11 @@ const TestLayer = Layer.mergeAll( ); const LegacyImportTestLayer = OrchestrationV2LayerLive.pipe( - Layer.provide(mcpSessionRegistryTestLayer), + Layer.provide(McpSessionRegistryTestkit.layer), Layer.provideMerge(SqlitePersistenceMemory), Layer.provide(CheckpointStoreTestLayer), Layer.provide(ServerConfigLayer), - Layer.provide(ServerSettingsService.layerTest()), + Layer.provide(ServerSettings.layerTest()), Layer.provide(TestProviderInstanceRegistry), Layer.provide(GitWorkflowTestLayer), Layer.provide(ProjectServiceTestLayer), @@ -247,10 +242,10 @@ const ProjectDeletionTestLayer = Layer.mergeAll( OrchestrationV2LayerLive.pipe(Layer.provide(ProjectServiceLayerLive)), ProjectServiceLayerLive, OrchestrationV2EventSinkLayerLive, - threadCommandExecutorLayer, + ThreadCommandExecutor.layer, ).pipe( Layer.provide( - Layer.mock(ProjectEnrichmentService)({ + Layer.mock(ProjectEnrichmentService.ProjectEnrichmentService)({ peek: () => Effect.succeed({ repositoryIdentity: null, @@ -267,15 +262,15 @@ const ProjectDeletionTestLayer = Layer.mergeAll( }), ), Layer.provide( - Layer.mock(WorkspacePaths)({ + Layer.mock(WorkspacePaths.WorkspacePaths)({ normalizeWorkspaceRoot: (workspaceRoot) => Effect.succeed(workspaceRoot), }), ), - Layer.provide(mcpSessionRegistryTestLayer), + Layer.provide(McpSessionRegistryTestkit.layer), Layer.provide(SqlitePersistenceMemory), Layer.provide(CheckpointStoreTestLayer), Layer.provide(ServerConfigLayer), - Layer.provide(ServerSettingsService.layerTest()), + Layer.provide(ServerSettings.layerTest()), Layer.provide(TestProviderInstanceRegistry), Layer.provide(GitWorkflowTestLayer), Layer.provide(PlatformTestLayer), @@ -285,9 +280,9 @@ it.layer(ProjectDeletionTestLayer)("project deletion during thread commands", (i it.effect("waits for an in-flight thread update before planning deletion", () => Effect.gen(function* () { const projects = yield* ProjectService.ProjectService; - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; - const executor = yield* ThreadCommandExecutor; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; + const executor = yield* ThreadCommandExecutor.ThreadCommandExecutor; const projectId = ProjectId.make("runtime-project-delete-concurrent"); const threadId = ThreadId.make("runtime-thread-delete-concurrent"); const updateCommandId = CommandId.make("runtime-thread-delete-concurrent-update"); @@ -328,7 +323,10 @@ it.layer(ProjectDeletionTestLayer)("project deletion during thread commands", (i ) : commitCommand(input), ); - const observeLock: ThreadCommandExecutor["Service"]["withLock"] = (key, effect) => { + const observeLock: ThreadCommandExecutor.ThreadCommandExecutor["Service"]["withLock"] = ( + key, + effect, + ) => { if (key !== threadId || ++threadLockRequests !== 2) return withLock(key, effect); return Deferred.succeed(deletionQueued, undefined).pipe( Effect.andThen(withLock(key, effect)), @@ -389,12 +387,12 @@ const SharedApplicationDataPlaneTestLayer = Layer.mergeAll( OrchestrationEventInfrastructureLayerLive, ).pipe( Layer.provide( - Layer.mock(WorkspacePaths)({ + Layer.mock(WorkspacePaths.WorkspacePaths)({ normalizeWorkspaceRoot: (workspaceRoot) => Effect.succeed(workspaceRoot), }), ), Layer.provide( - Layer.succeed(ProjectEnrichmentService, { + Layer.succeed(ProjectEnrichmentService.ProjectEnrichmentService, { peek: () => Effect.succeed({ repositoryIdentity: null, @@ -412,11 +410,11 @@ const SharedApplicationDataPlaneTestLayer = Layer.mergeAll( subscribeChanges: Effect.never, }), ), - Layer.provide(mcpSessionRegistryTestLayer), + Layer.provide(McpSessionRegistryTestkit.layer), Layer.provideMerge(SqlitePersistenceMemory), Layer.provide(CheckpointStoreTestLayer), Layer.provide(ServerConfigLayer), - Layer.provide(ServerSettingsService.layerTest()), + Layer.provide(ServerSettings.layerTest()), Layer.provide(TestProviderInstanceRegistry), Layer.provide(GitWorkflowTestLayer), Layer.provide(PlatformTestLayer), @@ -425,7 +423,7 @@ const SharedApplicationDataPlaneTestLayer = Layer.mergeAll( it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { it.effect("emits model updates separately from provider switches", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const threadId = ThreadId.make("runtime-layer-model-selection-events"); yield* orchestrator.dispatch({ @@ -473,9 +471,9 @@ it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { */ const seedFailingRollbackThread = (name: string) => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; - const outbox = yield* EffectOutboxV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const threadId = ThreadId.make(name); yield* orchestrator.dispatch({ type: "thread.create", @@ -553,9 +551,9 @@ it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { it.effect("projects a rollback that fails every attempt and clears it on the next one", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const outbox = yield* EffectOutboxV2; - const worker = yield* OrchestrationEffectWorkerV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; const { threadId, rollback } = yield* seedFailingRollbackThread("runtime-rollback-failure"); const rollbackCommandId = CommandId.make("runtime-rollback-failure-rollback"); @@ -582,9 +580,9 @@ it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { it.effect("ignores a late failure from a rollback that a newer one superseded", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const outbox = yield* EffectOutboxV2; - const worker = yield* OrchestrationEffectWorkerV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; const { threadId, rollback } = yield* seedFailingRollbackThread( "runtime-rollback-superseded", ); @@ -622,9 +620,9 @@ it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { it.effect("rejects non-ready rollback targets before persisting events or effects", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; - const outbox = yield* EffectOutboxV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; const threadId = ThreadId.make("runtime-rollback-readiness"); yield* orchestrator.dispatch({ type: "thread.create", @@ -765,7 +763,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { ); } else { const error = yield* rollback.pipe(Effect.flip); - assert.instanceOf(error, OrchestratorDispatchError); + assert.instanceOf(error, Orchestrator.OrchestratorDispatchError); assert.equal( error.cause, `Checkpoint ${checkpointId} is ${status} and cannot be restored.`, @@ -783,10 +781,10 @@ it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { it.effect("resolves delivery intent against the active run and starts after it completes", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; - const outbox = yield* EffectOutboxV2; - const sessions = yield* ProviderSessionManagerV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; + const outbox = yield* EffectOutbox.EffectOutboxV2; + const sessions = yield* ProviderSessionManager.ProviderSessionManagerV2; const threadId = ThreadId.make("runtime-delivery-intent"); yield* orchestrator.dispatch({ type: "thread.create", @@ -956,8 +954,8 @@ it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { it.effect("answers an async question after its provider exits and commits the answer once", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; const now = yield* DateTime.now; const threadId = ThreadId.make("runtime-async-question"); const requestId = RuntimeRequestId.make("runtime-async-question-request"); @@ -1108,8 +1106,8 @@ it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { it.effect("dismisses message-capable questions directly and while settling", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; const seedQuestion = Effect.fn("runtimeLayerTest.seedQuestion")(function* (name: string) { const threadId = ThreadId.make(`${name}-thread`); @@ -1251,8 +1249,8 @@ it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { it.effect("merges an explicit provider-finished run while checkpoint capture is pending", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; const now = yield* DateTime.now; const projectId = ProjectId.make("runtime-layer-waiting-merge-project"); const targetThreadId = ThreadId.make("runtime-layer-waiting-merge-target"); @@ -1423,10 +1421,10 @@ it.layer(LegacyImportTestLayer)("OrchestrationV2 legacy import", (it) => { it.effect("hydrates imported transcripts before commands and propagates hydration failures", () => Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - const importer = yield* LegacyV1ThreadImporter; - const maintenance = yield* ProjectionMaintenanceV2; - const orchestrator = yield* OrchestratorV2; - const threadManagement = yield* ThreadManagementService; + const importer = yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const threadManagement = yield* ThreadManagementService.ThreadManagementService; const metadataThreadId = ThreadId.make("runtime-layer-legacy-metadata-thread"); const failureThreadId = ThreadId.make("runtime-layer-legacy-failure-thread"); const projectId = ProjectId.make("runtime-layer-legacy-project"); @@ -1600,10 +1598,10 @@ it.layer(LegacyImportTestLayer)("OrchestrationV2 legacy import", (it) => { `.pipe(Effect.orDie), ), ); - assert.instanceOf(projectionFailure, OrchestratorProjectionError); - assert.instanceOf(projectionFailure.cause, LegacyV1ThreadImportError); - assert.instanceOf(hydrationFailure, OrchestratorDispatchError); - assert.instanceOf(hydrationFailure.cause, LegacyV1ThreadImportError); + assert.instanceOf(projectionFailure, Orchestrator.OrchestratorProjectionError); + assert.instanceOf(projectionFailure.cause, LegacyV1ThreadImporter.LegacyV1ThreadImportError); + assert.instanceOf(hydrationFailure, Orchestrator.OrchestratorDispatchError); + assert.instanceOf(hydrationFailure.cause, LegacyV1ThreadImporter.LegacyV1ThreadImportError); const projectionAfterFailure = yield* orchestrator.getThreadProjection(failureThreadId); assert.isNull(projectionAfterFailure.thread.archivedAt); @@ -1622,7 +1620,7 @@ it.layer(LegacyImportTestLayer)("OrchestrationV2 legacy import", (it) => { it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { it.effect("applies lifecycle commands idempotently and emits archive/removal shell deltas", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const threadId = ThreadId.make("runtime-layer-lifecycle-thread"); const projectId = ProjectId.make("runtime-layer-lifecycle-project"); const project = { @@ -1671,7 +1669,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { expectedWorktreePath: null, }) .pipe(Effect.flip); - assert.instanceOf(staleWorkspaceUpdate, OrchestratorDispatchError); + assert.instanceOf(staleWorkspaceUpdate, Orchestrator.OrchestratorDispatchError); const projectionAfterStaleWorkspaceUpdate = yield* orchestrator.getThreadProjection(threadId); assert.equal(projectionAfterStaleWorkspaceUpdate.thread.branch, "feature/v2"); assert.equal(projectionAfterStaleWorkspaceUpdate.thread.worktreePath, "/tmp/t3-v2-worktree"); @@ -1700,7 +1698,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { branchPullRequest: pullRequest, }) .pipe(Effect.flip); - assert.instanceOf(staleProjectWorkspace, OrchestratorDispatchError); + assert.instanceOf(staleProjectWorkspace, Orchestrator.OrchestratorDispatchError); yield* moveProject(projectId, project.workspaceRoot, "2026-09-07T00:02:00.000Z"); yield* orchestrator.dispatch({ type: "thread.pull-request.sync", @@ -1738,7 +1736,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { branchPullRequest: null, }) .pipe(Effect.flip); - assert.instanceOf(stalePullRequestSync, OrchestratorDispatchError); + assert.instanceOf(stalePullRequestSync, Orchestrator.OrchestratorDispatchError); yield* orchestrator.dispatch({ type: "thread.runtime-mode.set", commandId: CommandId.make("runtime-layer-lifecycle-runtime"), @@ -1778,7 +1776,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { ), }) .pipe(Effect.flip); - assert.instanceOf(staleAutoSettle, OrchestratorDispatchError); + assert.instanceOf(staleAutoSettle, Orchestrator.OrchestratorDispatchError); yield* orchestrator.dispatch({ type: "thread.auto-settle", commandId: CommandId.make("runtime-layer-lifecycle-auto-settle"), @@ -1804,7 +1802,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { snapshotAt: postUnsettleProjection.thread.updatedAt, }) .pipe(Effect.flip); - assert.instanceOf(overriddenAutoSettle, OrchestratorDispatchError); + assert.instanceOf(overriddenAutoSettle, Orchestrator.OrchestratorDispatchError); yield* orchestrator.dispatch({ type: "thread.settle", @@ -1910,8 +1908,8 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { it.effect("persists linked pull requests through projection rebuilds and unlinking", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const maintenance = yield* ProjectionMaintenanceV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; const threadId = ThreadId.make("runtime-layer-linked-pull-request-thread"); const linkedPullRequest = { projectId: ProjectId.make("runtime-layer-linked-pull-request-project"), @@ -1971,8 +1969,8 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { it.effect("keeps the branch pull request when linking another pull request", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const maintenance = yield* ProjectionMaintenanceV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; const threadId = ThreadId.make("branch-pr-link"); const projectId = ProjectId.make("branch-pr-project"); yield* seedProject({ @@ -2061,8 +2059,8 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { it.effect("retains multiple pull requests and dismissed stack members through rebuilds", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const maintenance = yield* ProjectionMaintenanceV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; const threadId = ThreadId.make("runtime-multiple-pull-requests"); yield* orchestrator.dispatch({ type: "thread.create", @@ -2152,7 +2150,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { it.effect("persists rejected command receipts across retries", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const command = { type: "thread.archive" as const, commandId: CommandId.make("runtime-layer-rejected-command"), @@ -2171,8 +2169,8 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { "admits restart continuations once and rejects a stale continuation behind newer work", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; const threadId = ThreadId.make("runtime-layer-restart-continuation"); yield* orchestrator.dispatch({ type: "thread.create", @@ -2251,8 +2249,8 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { it.effect("does not admit a restart continuation of a failed run that lost background work", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; const threadId = ThreadId.make("runtime-layer-restart-failed-source"); yield* orchestrator.dispatch({ type: "thread.create", @@ -2331,7 +2329,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { it.effect("rejects settling a thread while a run is active", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const threadId = ThreadId.make("runtime-layer-active-settle-thread"); yield* orchestrator.dispatch({ @@ -2375,7 +2373,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { expectedEmpty: true, }) .pipe(Effect.flip); - assert.instanceOf(nonEmptyClaim, OrchestratorDispatchError); + assert.instanceOf(nonEmptyClaim, Orchestrator.OrchestratorDispatchError); const error = yield* orchestrator .dispatch({ @@ -2396,8 +2394,8 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { it.effect("settles past held automatic runs but not held user messages", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; const threadId = ThreadId.make("runtime-layer-settle-automatic-queued"); yield* orchestrator.dispatch({ @@ -2521,7 +2519,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { it.effect("cancels queued work when a thread is archived", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const threadId = ThreadId.make("runtime-layer-archive-queued-thread"); yield* orchestrator.dispatch({ @@ -2606,8 +2604,8 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { `promotes only one queued run after each terminal run (notification: ${automatic})`, () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; const threadId = ThreadId.make(`runtime-layer-serialized-queue-thread-${automatic}`); yield* orchestrator.dispatch({ @@ -2810,8 +2808,8 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { "handles a queued message after a %s failure", (failureClass) => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; const threadId = ThreadId.make(`runtime-layer-failed-queue-${failureClass}`); yield* orchestrator.dispatch({ @@ -2945,7 +2943,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { it.effect("keeps the queue after a user interrupts the active run", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const threadId = ThreadId.make("runtime-layer-interrupted-queue"); yield* orchestrator.dispatch({ type: "thread.create", @@ -3000,8 +2998,8 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { `preserves and holds queued messages across ${trigger} until explicitly resumed`, () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const recovery = yield* ProviderRuntimeRecoveryService; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const recovery = yield* ProviderRuntimeRecoveryService.ProviderRuntimeRecoveryService; const threadId = ThreadId.make(`queue-hold-${trigger}`); yield* orchestrator.dispatch({ type: "thread.create", @@ -3037,7 +3035,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { yield* recovery.reconcile(trigger); // A second boot must preserve the hold, even when only queued work remains. yield* recovery.reconcile("startup"); - const maintenance = yield* ProjectionMaintenanceV2; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; assert.isTrue((yield* maintenance.rebuild).valid); assert.equal(yield* orchestrator.resumeQueuedRuns, 0); const held = yield* orchestrator.getThreadProjection(threadId); @@ -3104,7 +3102,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { it.effect("edits and removes queued runs", () => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const threadId = ThreadId.make("runtime-layer-queued-edit-thread"); yield* orchestrator.dispatch({ @@ -3261,9 +3259,9 @@ it.layer(SharedApplicationDataPlaneTestLayer)("pending provider interruption", ( it.effect("interrupts a pending provider start without launching provider work", () => Effect.gen(function* () { const projects = yield* ProjectService.ProjectService; - const orchestrator = yield* OrchestratorV2; - const threadManagement = yield* ThreadManagementService; - const effectWorker = yield* OrchestrationEffectWorkerV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const threadManagement = yield* ThreadManagementService.ThreadManagementService; + const effectWorker = yield* EffectWorker.OrchestrationEffectWorkerV2; const projectId = ProjectId.make("runtime-layer-pending-interrupt-project"); const threadId = ThreadId.make("runtime-layer-pending-interrupt-thread"); @@ -3335,7 +3333,7 @@ it.layer(SharedApplicationDataPlaneTestLayer)("snooze projection", (it) => { it.effect("carries snooze state through the V2 shell projection", () => Effect.gen(function* () { const projects = yield* ProjectService.ProjectService; - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const projectId = ProjectId.make("runtime-layer-snoozed-project"); const threadId = ThreadId.make("runtime-layer-snoozed-thread"); const snoozedUntil = "2099-07-25T09:00:00.000Z"; @@ -3410,7 +3408,7 @@ it.layer(SharedApplicationDataPlaneTestLayer)("visited projection", (it) => { it.effect("carries the visited watermark through the V2 shell projection", () => Effect.gen(function* () { const projects = yield* ProjectService.ProjectService; - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const projectId = ProjectId.make("runtime-layer-visited-project"); const threadId = ThreadId.make("runtime-layer-visited-thread"); const visitedAt = "2026-07-24T01:00:00.000Z"; @@ -3477,7 +3475,7 @@ it.layer(SharedApplicationDataPlaneTestLayer)("visited projection", (it) => { threadId, }) .pipe(Effect.flip); - assert.instanceOf(markUnread, OrchestratorDispatchError); + assert.instanceOf(markUnread, Orchestrator.OrchestratorDispatchError); // A read receipt must not decode any transcript, including inherited or // unreadable historical rows. It only advances the thread's watermark. @@ -3509,7 +3507,7 @@ it.layer(SharedApplicationDataPlaneTestLayer)("visited projection", (it) => { visitedAt: "invalid-timestamp", }) .pipe(Effect.flip); - assert.instanceOf(invalidVisit, OrchestratorDispatchError); + assert.instanceOf(invalidVisit, Orchestrator.OrchestratorDispatchError); }), ); }); @@ -3518,8 +3516,8 @@ it.layer(SharedApplicationDataPlaneTestLayer)("shared application data plane", ( it.effect("orders retained project transactions and V2 thread transactions in one source", () => Effect.gen(function* () { const projects = yield* ProjectService.ProjectService; - const applicationEvents = yield* OrchestrationEventStore; - const orchestrator = yield* OrchestratorV2; + const applicationEvents = yield* OrchestrationEventStore.OrchestrationEventStore; + const orchestrator = yield* Orchestrator.OrchestratorV2; const sql = yield* SqlClient.SqlClient; const projectId = ProjectId.make("runtime-layer-shared-project"); const threadId = ThreadId.make("runtime-layer-shared-thread"); @@ -3594,8 +3592,8 @@ it.layer(TestLayer)("usage-limit recovery", (it) => { "manually resumes an %s run ahead of its queued message only once", (reason) => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const events = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const events = yield* EventSink.EventSinkV2; const threadId = ThreadId.make(`manual-resume:${reason}`); const projectId = ProjectId.make(`manual-resume:project:${reason}`); const now = yield* DateTime.now; @@ -3772,8 +3770,8 @@ it.layer(TestLayer)("usage-limit recovery", (it) => { "wake", ] as const)("guards a scheduled usage-limit continuation against %s", (scenario) => Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const events = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const events = yield* EventSink.EventSinkV2; const threadId = ThreadId.make(`recovery:${scenario}`); const projectId = ProjectId.make(`recovery:project:${scenario}`); yield* seedProject({ From 8992cd7fa5028484647bf465f2b458e838ce0d3f Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 30 Sep 2026 01:35:56 -0700 Subject: [PATCH 2/4] refactor(server): import services as namespaces in orchestration-v2 adapters Replace named tag and layer imports (and layer aliases) with module namespaces in orchestration-v2/Adapters. Imports and call sites only. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../Adapters/AcpAdapterV2.test.ts | 346 ++++++------ .../orchestration-v2/Adapters/AcpAdapterV2.ts | 181 +++---- .../Adapters/AcpRegistryAdapterV2.test.ts | 22 +- .../Adapters/AcpRegistryAdapterV2.testkit.ts | 16 +- .../Adapters/AcpRegistryAdapterV2.ts | 38 +- .../Adapters/AntigravityAdapterV2.test.ts | 18 +- .../Adapters/AntigravityAdapterV2.ts | 3 +- .../Adapters/ClaudeAdapterV2.test.ts | 496 +++++++++--------- .../Adapters/ClaudeAdapterV2.testkit.ts | 167 +++--- .../Adapters/ClaudeAdapterV2.ts | 197 ++++--- .../Adapters/CodexAdapterV2.test.ts | 331 ++++++------ .../Adapters/CodexAdapterV2.testkit.ts | 33 +- .../Adapters/CursorAdapterV2.test.ts | 28 +- .../Adapters/CursorAdapterV2.testkit.ts | 87 ++- .../Adapters/CursorAdapterV2.ts | 309 +++++------ .../Adapters/CursorAgentSdk.test.ts | 26 +- .../Adapters/CursorAgentSdk.ts | 6 +- .../Adapters/GrokAdapterV2.test.ts | 23 +- .../Adapters/GrokAdapterV2.testkit.ts | 16 +- .../Adapters/GrokAdapterV2.ts | 50 +- .../Adapters/OpenCodeAdapterV2.test.ts | 70 +-- .../Adapters/OpenCodeAdapterV2.testkit.ts | 46 +- .../Adapters/PiAdapterV2.test.ts | 10 +- .../Adapters/PiAdapterV2.testkit.ts | 8 +- .../orchestration-v2/Adapters/PiAdapterV2.ts | 130 ++--- 25 files changed, 1286 insertions(+), 1371 deletions(-) diff --git a/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts index 562fc5cdf21f..33b91614b4da 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts @@ -54,7 +54,7 @@ import * as EffectAcpErrors from "effect-acp/errors"; import type * as EffectAcpProtocol from "effect-acp/protocol"; import type * as EffectAcpSchema from "effect-acp/compat"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import * as McpProviderSession from "../../mcp/McpProviderSession.ts"; import * as AcpSessionRuntime from "../../provider/acp/AcpSessionRuntime.ts"; import { @@ -64,7 +64,7 @@ import { normalizeXAiAcpToolCallState, registerXAiBackgroundTaskTracking, } from "../../provider/acp/XAiAcpExtension.ts"; -import { layer as idAllocatorLayer, IdAllocatorV2 } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { ProviderAdapterProtocolError, ProviderAdapterV2RuntimePolicy, @@ -104,7 +104,7 @@ const serverConfigLayer = ServerConfig.layerTest(process.cwd(), { prefix: "t3-acp-v2-adapter-", }).pipe(Layer.provide(NodeServices.layer)); -const testLayer = Layer.mergeAll(NodeServices.layer, idAllocatorLayer, serverConfigLayer); +const testLayer = Layer.mergeAll(NodeServices.layer, IdAllocator.layer, serverConfigLayer); const ACP_TEST_DRIVER = ProviderDriverKind.make("acp-test"); const decodeUnknownJson = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Unknown)); @@ -614,8 +614,8 @@ describe("AcpAdapterV2", () => { crypto: yield* Crypto.Crypto, instanceId, fileSystem: yield* FileSystem.FileSystem, - idAllocator: yield* IdAllocatorV2, - serverConfig: yield* ServerConfig, + idAllocator: yield* IdAllocator.IdAllocatorV2, + serverConfig: yield* ServerConfig.ServerConfig, selfInvocation: yield* resolveSelfInvocation(), flavor: { driver: ProviderDriverKind.make("acpRegistry"), @@ -711,9 +711,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation().pipe( Effect.provideService(HostProcessIsExecutable, true), ); @@ -784,9 +784,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -911,9 +911,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -1001,7 +1001,7 @@ describe("AcpAdapterV2", () => { it.effect("keeps Devin parent paragraphs intact while projecting native child work", () => Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -1014,7 +1014,7 @@ describe("AcpAdapterV2", () => { crypto: yield* Crypto.Crypto, fileSystem: yield* FileSystem.FileSystem, idAllocator, - serverConfig: yield* ServerConfig, + serverConfig: yield* ServerConfig.ServerConfig, selfInvocation: yield* resolveSelfInvocation(), flavor: { driver: ACP_TEST_DRIVER, @@ -1225,9 +1225,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -1384,9 +1384,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -1469,9 +1469,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -1615,9 +1615,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -1698,9 +1698,9 @@ describe("AcpAdapterV2", () => { if ((yield* HostProcessPlatform) !== "linux") return; const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -1783,9 +1783,9 @@ describe("AcpAdapterV2", () => { if ((yield* HostProcessPlatform) !== "linux") return; const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -1839,9 +1839,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -1917,9 +1917,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -2129,9 +2129,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -2226,9 +2226,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -2363,9 +2363,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -2425,9 +2425,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -2592,9 +2592,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -2753,9 +2753,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -2869,9 +2869,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -2919,9 +2919,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -2975,9 +2975,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -3056,9 +3056,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -3105,9 +3105,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -3216,9 +3216,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -3363,9 +3363,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const protocolEvents = yield* Queue.unbounded(); const native: { current?: AcpSessionRuntime.AcpSessionRuntime["Service"] } = {}; @@ -3477,9 +3477,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -3578,9 +3578,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -3677,9 +3677,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -3766,9 +3766,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -3834,9 +3834,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -3942,9 +3942,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -4056,9 +4056,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -4197,9 +4197,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -4313,9 +4313,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -4418,9 +4418,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -4511,9 +4511,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -4604,9 +4604,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -4717,9 +4717,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -4820,9 +4820,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -4918,9 +4918,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -5026,9 +5026,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -5186,9 +5186,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -5345,9 +5345,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -5594,9 +5594,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -5825,9 +5825,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -6044,9 +6044,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -6192,9 +6192,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -6444,9 +6444,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -6744,9 +6744,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -6987,9 +6987,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -7216,9 +7216,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -7499,9 +7499,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -7753,9 +7753,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -7941,9 +7941,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -8031,9 +8031,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -8225,9 +8225,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -8372,9 +8372,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -8503,9 +8503,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -8807,9 +8807,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -9004,9 +9004,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -9174,9 +9174,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -9305,9 +9305,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -9425,9 +9425,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -9634,9 +9634,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -9800,9 +9800,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -10002,9 +10002,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -10242,9 +10242,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -10461,9 +10461,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -10676,9 +10676,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -11048,9 +11048,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -11202,9 +11202,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -11533,9 +11533,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -11720,9 +11720,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -11964,9 +11964,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -12154,9 +12154,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -12400,9 +12400,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -12618,9 +12618,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -12707,9 +12707,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -12820,9 +12820,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -13182,9 +13182,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -13307,9 +13307,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -13441,9 +13441,9 @@ describe("AcpAdapterV2", () => { if ((yield* HostProcessPlatform) !== "linux") return; const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -13718,9 +13718,9 @@ describe("AcpAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -13884,9 +13884,9 @@ describe("AcpAdapterV2", () => { if ((yield* HostProcessPlatform) !== "linux") return; const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const selfInvocation = yield* resolveSelfInvocation(); const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), diff --git a/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts index 938144e98062..4c4de1f7aeb0 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts @@ -57,7 +57,7 @@ import type * as EffectAcpSchema from "effect-acp/compat"; import { formatReadToolLabel, formatSearchToolLabel } from "@t3tools/shared/toolActivity"; import { resolveAttachmentPath } from "../../attachmentStore.ts"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import { makeAcpMcpOverAcpBridge, type AcpMcpOverAcpBridge, @@ -76,10 +76,6 @@ import { type AcpSessionModeState, type AcpToolCallState, } from "../../provider/acp/AcpRuntimeModel.ts"; -import type { - AcpSessionRuntimeOptions, - AcpSessionRuntimeStartResult, -} from "../../provider/acp/AcpSessionRuntime.ts"; import { acpClientExecuteDisposition, acpMcpToolApprovalElicitationDisposition, @@ -100,7 +96,7 @@ import { type T3AcpInstructionState, } from "../../provider/T3OrchestrationInstructions.ts"; import { buildRuntimeInstructions } from "../../provider/RuntimeInstructions.ts"; -import { IdAllocatorV2, type IdAllocatorV2Shape } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { type ProviderContinuationRequest } from "../ProviderContinuationRequests.ts"; import { type BackgroundWork, @@ -118,28 +114,7 @@ import { makeSubagentConversationArtifacts, subagentThreadTitle, } from "../SubagentProjection.ts"; -import { - ProviderAdapterEnsureThreadError, - ProviderAdapterForkThreadError, - ProviderAdapterInterruptError, - ProviderAdapterOpenSessionError, - ProviderAdapterProtocolError, - ProviderAdapterReadThreadSnapshotError, - ProviderAdapterResumeThreadError, - ProviderAdapterRollbackThreadError, - ProviderAdapterRuntimeRequestResponseError, - ProviderAdapterSteerRunUnsupportedError, - ProviderAdapterTurnStartError, - ProviderAdapterV2, - type ProviderAdapterV2EnsureThreadInput, - type ProviderAdapterV2Event, - type ProviderAdapterV2InterruptInput, - type ProviderAdapterV2OpenSessionInput, - type ProviderAdapterV2RuntimePolicy, - type ProviderAdapterV2SessionRuntime, - type ProviderAdapterV2Shape, - type ProviderAdapterV2TurnInput, -} from "../ProviderAdapter.ts"; +import * as ProviderAdapter from "../ProviderAdapter.ts"; export const ACP_PROTOCOL = "acp.ndjson-jsonrpc" as const; @@ -158,7 +133,7 @@ export interface AcpAdapterV2RuntimeInput { * Policy the session opened with. A runtime-mode change reopens the session, * so flavors that encode permissions in the launch command (Grok) read it here. */ - readonly runtimePolicy: ProviderAdapterV2RuntimePolicy; + readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy; readonly mcpServers: ReadonlyArray; readonly acpMcpServers?: ReadonlyArray; /** Scoped credentials for terminal fallback when an ACP agent drops `mcpServers`. */ @@ -166,16 +141,18 @@ export interface AcpAdapterV2RuntimeInput { readonly resumeSessionId?: string; readonly interruptPromptOnCancel?: boolean; readonly clientCapabilities: EffectAcpSchema.InitializeRequest["clientCapabilities"]; - readonly clientInfo: AcpSessionRuntimeOptions["clientInfo"]; - readonly requestLogger?: NonNullable; - readonly protocolLogging: NonNullable; - readonly onTermination: NonNullable; - readonly onOutgoingResponseFailure?: AcpSessionRuntimeOptions["onOutgoingResponseFailure"]; - readonly onOutgoingResponse?: AcpSessionRuntimeOptions["onOutgoingResponse"]; + readonly clientInfo: AcpSessionRuntime.AcpSessionRuntimeOptions["clientInfo"]; + readonly requestLogger?: NonNullable; + readonly protocolLogging: NonNullable< + AcpSessionRuntime.AcpSessionRuntimeOptions["protocolLogging"] + >; + readonly onTermination: NonNullable; + readonly onOutgoingResponseFailure?: AcpSessionRuntime.AcpSessionRuntimeOptions["onOutgoingResponseFailure"]; + readonly onOutgoingResponse?: AcpSessionRuntime.AcpSessionRuntimeOptions["onOutgoingResponse"]; } export type AcpAdapterV2NativeLogging = Pick< - AcpSessionRuntimeOptions, + AcpSessionRuntime.AcpSessionRuntimeOptions, "requestLogger" | "protocolLogging" >; @@ -270,11 +247,13 @@ export interface AcpAdapterV2Flavor { */ readonly applyModelSelection?: (input: { readonly runtime: AcpSessionRuntime.AcpSessionRuntime["Service"]; - readonly startResult: AcpSessionRuntimeStartResult; + readonly startResult: AcpSessionRuntime.AcpSessionRuntimeStartResult; readonly modelSelection: ModelSelection; }) => Effect.Effect; /** Native session mode to select for a runtime policy (e.g. Antigravity `yolo`). */ - readonly sessionModeForPolicy?: (policy: ProviderAdapterV2RuntimePolicy) => string | undefined; + readonly sessionModeForPolicy?: ( + policy: ProviderAdapter.ProviderAdapterV2RuntimePolicy, + ) => string | undefined; /** * Opts the session into the ACP client `fs` capability. Agents read and write * files themselves under their own permission model unless a flavor sets @@ -312,7 +291,7 @@ export interface AcpAdapterV2Flavor { * the user instead of being approved by T3's policy. */ readonly permissionDisposition?: ( - policy: ProviderAdapterV2RuntimePolicy, + policy: ProviderAdapter.ProviderAdapterV2RuntimePolicy, request: EffectAcpSchema.RequestPermissionRequest, ) => AcpPermissionDisposition; /** Approval choices to advertise on the approval card for a permission request. */ @@ -492,8 +471,8 @@ export interface AcpAdapterV2Options { readonly flavor: AcpAdapterV2Flavor; readonly crypto: Crypto.Crypto; readonly fileSystem: FileSystem.FileSystem; - readonly idAllocator: IdAllocatorV2Shape; - readonly serverConfig: ServerConfig["Service"]; + readonly idAllocator: IdAllocator.IdAllocatorV2Shape; + readonly serverConfig: ServerConfig.ServerConfig["Service"]; /** How agents spawn this install's `acp-mcp-bridge`; see `resolveSelfInvocation`. */ readonly selfInvocation: SelfInvocation; /** @@ -648,7 +627,7 @@ export const AcpProviderCapabilitiesV2 = { function negotiatedCapabilities( base: OrchestrationV2ProviderCapabilities, - started: AcpSessionRuntimeStartResult, + started: AcpSessionRuntime.AcpSessionRuntimeStartResult, ): OrchestrationV2ProviderCapabilities { const agent = started.initializeResult.agentCapabilities ?? {}; const session = agent.sessionCapabilities; @@ -742,11 +721,11 @@ function acpMcpActivation(threadId: ThreadId | null, self: SelfInvocation) { function nativeThreadId( driver: ProviderDriverKind, thread: OrchestrationV2ProviderThread, -): Effect.Effect { +): Effect.Effect { const id = thread.nativeThreadRef?.nativeId; if (id === null || id === undefined || id.trim().length === 0) { return Effect.fail( - new ProviderAdapterProtocolError({ + new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: `Provider thread ${thread.id} is missing its ACP session id`, }), @@ -758,7 +737,7 @@ function nativeThreadId( function makeProviderThread(input: { readonly driver: ProviderDriverKind; readonly providerInstanceId: ProviderInstanceId; - readonly idAllocator: IdAllocatorV2Shape; + readonly idAllocator: IdAllocator.IdAllocatorV2Shape; readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"]; readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"]; readonly nativeThreadId: string; @@ -1106,7 +1085,7 @@ interface AcpNativeBuildConfiguration { } interface ActiveAcpTurn { - readonly input: ProviderAdapterV2TurnInput; + readonly input: ProviderAdapter.ProviderAdapterV2TurnInput; readonly providerTurnId: OrchestrationV2ProviderTurn["id"]; readonly nativeThreadId: string; readonly nativeTurnId: string; @@ -1186,9 +1165,9 @@ type AcpRuntimeTeardownState = | { readonly _tag: "Idle" } | { readonly _tag: "InProgress"; - readonly completed: Deferred.Deferred; + readonly completed: Deferred.Deferred; } - | { readonly _tag: "Failed"; readonly error: ProviderAdapterProtocolError }; + | { readonly _tag: "Failed"; readonly error: ProviderAdapter.ProviderAdapterProtocolError }; /** True when a root session/update carries ingestible turn output, not keepalive noise. */ function acpRootSessionUpdateIngestsOutput( @@ -1419,20 +1398,22 @@ interface SnapshotMessageState { loadingIndex: number; } -export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV2Shape { +export function makeAcpAdapterV2( + options: AcpAdapterV2Options, +): ProviderAdapter.ProviderAdapterV2Shape { const { flavor, fileSystem, idAllocator, serverConfig, selfInvocation: self } = options; const driver = flavor.driver; const continuationRequests = options.continuationRequests; const postSettleContinuationEnabled = flavor.enablePostSettleContinuation === true && continuationRequests !== undefined; - return ProviderAdapterV2.of({ + return ProviderAdapter.ProviderAdapterV2.of({ instanceId: options.instanceId, driver, getCapabilities: () => Effect.succeed(flavor.capabilities), planSelectionTransition: (input) => Effect.succeed(acpSelectionTransition(input)), openSession: Effect.fn("AcpAdapterV2.openSession")( - function* (input: ProviderAdapterV2OpenSessionInput) { + function* (input: ProviderAdapter.ProviderAdapterV2OpenSessionInput) { const sessionScope = yield* Effect.scope; // Persisted ACP threads from before item identity v2 retain their old // deterministic ids. Fresh threads scope native ids by instance so @@ -1594,8 +1575,9 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV // are policy-checked against the active turn policy; a command the user // already approved satisfies an "ask" disposition. const clientPolicyGrants = makeAcpClientPolicyGrants(); - let latestRuntimePolicy: ProviderAdapterV2RuntimePolicy = input.runtimePolicy; - const events = yield* Queue.unbounded(); + let latestRuntimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy = + input.runtimePolicy; + const events = yield* Queue.unbounded(); const activeTurn = yield* Ref.make(null); const activeSessionId = yield* Ref.make(null); const contextUsageBySessionId = yield* Ref.make( @@ -1614,7 +1596,8 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV readonly sessionId: string; readonly error: EffectAcpErrors.AcpError; } | null>(null); - const activeSessionSetup = yield* Ref.make(null); + const activeSessionSetup = + yield* Ref.make(null); const activeSelection = yield* Ref.make(null); const activeInteractionMode = yield* Ref.make(null); const promptInstructionStates = yield* Ref.make(new Map()); @@ -1993,7 +1976,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV } }); - const emitProviderEvent = (event: ProviderAdapterV2Event) => + const emitProviderEvent = (event: ProviderAdapter.ProviderAdapterV2Event) => Queue.offer(events, event).pipe(Effect.asVoid); let scheduleDeferredFinalize: (context: ActiveAcpTurn) => Effect.Effect = () => Effect.void; @@ -5864,7 +5847,9 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV const startReplacementAcpRuntime = Effect.fnUntraced(function* ( threadId: ThreadId | null, - commitSessionState: (replacement: AcpSessionRuntimeStartResult) => Effect.Effect, + commitSessionState: ( + replacement: AcpSessionRuntime.AcpSessionRuntimeStartResult, + ) => Effect.Effect, ) { const previousScope = runtimeScope; const previousGeneration = yield* Ref.get(runtimeCallbackGeneration); @@ -6076,7 +6061,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV ? yield* runtime.loadSession(sessionId, activationOptions) : canResumeSession ? yield* runtime.resumeSession(sessionId, activationOptions) - : yield* new ProviderAdapterProtocolError({ + : yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: `ACP driver cannot load or resume session ${sessionId}`, }); @@ -6085,9 +6070,9 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV }); const configureSession = Effect.fnUntraced(function* ( - startResult: AcpSessionRuntimeStartResult, + startResult: AcpSessionRuntime.AcpSessionRuntimeStartResult, modelSelection: ModelSelection, - runtimePolicy: ProviderAdapterV2RuntimePolicy, + runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy, ) { const requestedModel = flavor.resolveModelId?.(modelSelection) ?? modelSelection.model; let appliedModel: string | undefined; @@ -6585,7 +6570,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV }); const resolvePromptParts = Effect.fnUntraced(function* ( - turnInput: ProviderAdapterV2TurnInput, + turnInput: ProviderAdapter.ProviderAdapterV2TurnInput, sessionId: string, ) { const prompt: Array = []; @@ -6613,7 +6598,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV isProviderNativeImageAttachment, ); if (imageAttachments.length > 0 && !supportsImagePrompts) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: "ACP driver did not negotiate image prompt support", }); @@ -6624,7 +6609,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV attachment: attachment as ChatAttachment, }); if (path === null) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: `Invalid attachment id '${attachment.id}'`, }); @@ -6632,7 +6617,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV const bytes = yield* fileSystem.readFile(path).pipe( Effect.mapError( (cause) => - new ProviderAdapterProtocolError({ + new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: `Failed to read attachment '${attachment.id}'`, payload: cause, @@ -6646,7 +6631,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV }); } if (prompt.length === 0) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: "ACP turn requires non-empty text or attachments", }); @@ -6683,11 +6668,11 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV }); const startTurnUnlocked = Effect.fn("AcpAdapterV2.startTurn")( - function* (turnInput: ProviderAdapterV2TurnInput) { + function* (turnInput: ProviderAdapter.ProviderAdapterV2TurnInput) { yield* awaitRuntimeTeardown(); const existing = yield* Ref.get(activeTurn); if (existing !== null) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: `ACP provider turn ${existing.providerTurnId} is still active`, }); @@ -6720,7 +6705,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV ) { const currentSessionSetup = yield* Ref.get(activeSessionSetup); if (currentSessionSetup === null) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: `ACP session ${requestedSessionId} has no active setup metadata`, }); @@ -7051,7 +7036,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterTurnStartError({ + new ProviderAdapter.ProviderAdapterTurnStartError({ driver, threadId: turnInput.threadId, providerThreadId: turnInput.providerThread.id, @@ -7063,7 +7048,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV ); const startTurn = Effect.fn("AcpAdapterV2.startTurn.transition")(function* ( - turnInput: ProviderAdapterV2TurnInput, + turnInput: ProviderAdapter.ProviderAdapterV2TurnInput, ) { return yield* runtimeTransitionPermit.withPermit(startTurnUnlocked(turnInput)); }); @@ -7122,7 +7107,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV }), ); - const sessionRuntime: ProviderAdapterV2SessionRuntime = { + const sessionRuntime: ProviderAdapter.ProviderAdapterV2SessionRuntime = { instanceId: options.instanceId, driver, providerSessionId: input.providerSessionId, @@ -7160,11 +7145,11 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV } : {}), ensureThread: Effect.fn("AcpAdapterV2.ensureThread")( - function* (threadInput: ProviderAdapterV2EnsureThreadInput) { + function* (threadInput: ProviderAdapter.ProviderAdapterV2EnsureThreadInput) { const now = yield* DateTime.now; const sessionId = yield* Ref.get(activeSessionId); if (sessionId === null) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: "ACP runtime did not produce a session id", }); @@ -7188,7 +7173,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterEnsureThreadError({ + new ProviderAdapter.ProviderAdapterEnsureThreadError({ driver, threadId: threadInput.threadId, cause, @@ -7200,7 +7185,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV function* (threadInput: { readonly providerThread: OrchestrationV2ProviderThread; readonly modelSelection?: ModelSelection; - readonly runtimePolicy?: ProviderAdapterV2RuntimePolicy; + readonly runtimePolicy?: ProviderAdapter.ProviderAdapterV2RuntimePolicy; }) { return yield* runtimeTransitionPermit.withPermit( Effect.gen(function* () { @@ -7248,7 +7233,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterResumeThreadError({ + new ProviderAdapter.ProviderAdapterResumeThreadError({ driver, providerSessionId: input.providerSessionId, providerThreadId: threadInput.providerThread.id, @@ -7260,7 +7245,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV startTurn, ...(flavor.supportsCompaction === true ? { - compactThread: (turnInput: ProviderAdapterV2TurnInput) => + compactThread: (turnInput: ProviderAdapter.ProviderAdapterV2TurnInput) => startTurn({ ...turnInput, message: { ...turnInput.message, text: "/compact" }, @@ -7269,13 +7254,13 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV : {}), steerTurn: (turnInput) => Effect.fail( - new ProviderAdapterSteerRunUnsupportedError({ + new ProviderAdapter.ProviderAdapterSteerRunUnsupportedError({ driver, providerThreadId: turnInput.providerThread.id, }), ), interruptTurn: Effect.fn("AcpAdapterV2.interruptTurn")( - function* (turnInput: ProviderAdapterV2InterruptInput) { + function* (turnInput: ProviderAdapter.ProviderAdapterV2InterruptInput) { return yield* Effect.uninterruptibleMask((restore) => Effect.gen(function* () { yield* restore(runtimeTransitionPermit.take(1)); @@ -7367,7 +7352,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV if (containOrphanRuntime) { const teardownBarrier = yield* Deferred.make< void, - ProviderAdapterProtocolError + ProviderAdapter.ProviderAdapterProtocolError >(); yield* runtimeCallbackPermit.withPermit( Effect.gen(function* () { @@ -7389,7 +7374,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV yield* cancelPendingRuntimeRequests(); yield* terminalizeCarryoverSubagents(orphanCarryover); if (runtime.terminateProcessGroup === undefined) { - const error = new ProviderAdapterProtocolError({ + const error = new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: "ACP runtime does not expose its required process-group teardown; the session is poisoned", @@ -7402,7 +7387,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV Effect.exit, ); if (Exit.isFailure(teardownExit)) { - const error = new ProviderAdapterProtocolError({ + const error = new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: "ACP orphan runtime process-group teardown failed; the session is poisoned", @@ -7419,7 +7404,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV return undefined; } const teardownBarrier = hardRestart - ? yield* Deferred.make() + ? yield* Deferred.make() : null; if (teardownBarrier !== null) { yield* runtimeCallbackPermit.withPermit( @@ -7460,7 +7445,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV detail: string, payload?: unknown, ) { - const error = new ProviderAdapterProtocolError({ + const error = new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail, ...(payload === undefined ? {} : { payload }), @@ -7533,7 +7518,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV if (!context.finalized) { yield* finalizeTurn(context, "interrupted"); } - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: `ACP provider turn ${turnInput.providerTurnId} did not acknowledge cancellation before the interrupt timeout`, }); @@ -7564,7 +7549,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterInterruptError({ + new ProviderAdapter.ProviderAdapterInterruptError({ driver, providerThreadId: turnInput.providerThread.id, providerTurnId: turnInput.providerTurnId, @@ -7584,7 +7569,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV String(requestInput.requestId), ); if (pending === undefined || pending.generation !== generation) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: `No pending ACP runtime request ${requestInput.requestId}`, }); @@ -7593,13 +7578,13 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV pending.type === "user_input" ? yield* Deferred.succeed(pending.answers, requestInput.answers ?? null) : requestInput.decision === undefined - ? yield* new ProviderAdapterProtocolError({ + ? yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: `ACP approval request ${requestInput.requestId} requires a decision`, }) : yield* Deferred.succeed(pending.decision, requestInput.decision); if (!settled) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: `ACP runtime request ${requestInput.requestId} was already resolved`, }); @@ -7613,7 +7598,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV ).pipe( Effect.mapError( (cause) => - new ProviderAdapterRuntimeRequestResponseError({ + new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({ driver, requestId: requestInput.requestId, cause, @@ -7632,7 +7617,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV const sessionId = yield* nativeThreadId(driver, snapshotInput.providerThread); if ((yield* Ref.get(activeSessionId)) !== sessionId) { if (!capabilities.threads.canReadThreadSnapshot) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: "ACP driver does not support session/load snapshots", }); @@ -7682,7 +7667,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterReadThreadSnapshotError({ + new ProviderAdapter.ProviderAdapterReadThreadSnapshotError({ driver, providerThreadId: snapshotInput.providerThread.id, cause, @@ -7696,7 +7681,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV Effect.gen(function* () { const currentTurn = yield* Ref.get(activeTurn); if (currentTurn !== null) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: `Cannot roll back ACP provider thread ${rollbackInput.providerThread.id} while turn ${currentTurn.providerTurnId} is active`, }); @@ -7774,7 +7759,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV .pipe( Effect.mapError( (cause) => - new ProviderAdapterRollbackThreadError({ + new ProviderAdapter.ProviderAdapterRollbackThreadError({ driver, providerThreadId: rollbackInput.providerThread.id, checkpointId: rollbackInput.target.checkpointId, @@ -7792,13 +7777,13 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV forkInput.sourceProviderThread.appThreadId, ); if (!capabilities.threads.canForkThread) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: "ACP driver did not negotiate session/fork", }); } if (forkInput.providerTurnId !== undefined) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail: "ACP session/fork can only fork the current session head", }); @@ -7849,7 +7834,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterForkThreadError({ + new ProviderAdapter.ProviderAdapterForkThreadError({ driver, providerThreadId: forkInput.sourceProviderThread.id, cause, @@ -7864,7 +7849,7 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterOpenSessionError({ + new ProviderAdapter.ProviderAdapterOpenSessionError({ driver, providerSessionId: input.providerSessionId, cause, @@ -7875,4 +7860,4 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV }); } -export type AcpAdapterV2Env = FileSystem.FileSystem | IdAllocatorV2; +export type AcpAdapterV2Env = FileSystem.FileSystem | IdAllocator.IdAllocatorV2; diff --git a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts index 4ab22ed6eb58..e822ea4efd4d 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts @@ -14,15 +14,15 @@ import { HttpClient, HttpClientResponse } from "effect/unstable/http"; import { ChildProcessSpawner } from "effect/unstable/process"; import * as EffectAcpErrors from "effect-acp/errors"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import type { AcpRegistryAvailableCommands, AcpRegistryLiveConfiguration, } from "../../provider/acp/AcpRegistryProbe.ts"; -import { makeAcpRegistryCatalog } from "../../provider/acp/AcpRegistrySupport.ts"; +import * as AcpRegistrySupport from "../../provider/acp/AcpRegistrySupport.ts"; import { ACP_SESSION_MODE_OPTION_ID } from "../../provider/acp/AcpSessionConfig.ts"; import * as AcpSessionRuntime from "../../provider/acp/AcpSessionRuntime.ts"; -import { layer as idAllocatorLayer, IdAllocatorV2 } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { decodeAcpReplayTranscript, makeAcpReplayCompletenessAssertion, @@ -84,7 +84,7 @@ const registryLayer = Layer.succeed( const testLayer = Layer.mergeAll( NodeServices.layer, - idAllocatorLayer, + IdAllocator.layer, serverConfigLayer, registryLayer, ); @@ -196,9 +196,9 @@ describe("AcpRegistryAdapterV2", () => { environment: {}, childProcessSpawner, fileSystem, - idAllocator: yield* IdAllocatorV2, + idAllocator: yield* IdAllocator.IdAllocatorV2, resolver: { resolve: () => Effect.die("the runtime is injected") }, - serverConfig: yield* ServerConfig, + serverConfig: yield* ServerConfig.ServerConfig, makeRuntime: makeAcpReplayRuntime({ transcript, statusPath, @@ -266,9 +266,9 @@ describe("AcpRegistryAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), ); @@ -342,13 +342,13 @@ describe("AcpRegistryAdapterV2", () => { Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), ); - const resolver = yield* makeAcpRegistryCatalog({ + const resolver = yield* AcpRegistrySupport.makeAcpRegistryCatalog({ cacheDir: serverConfig.providerStatusCacheDir, toolsDir: serverConfig.baseDir + "/tools", registryUrl, diff --git a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.testkit.ts index 79a2e5606913..113b5b10a725 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.testkit.ts @@ -9,9 +9,9 @@ import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; import { ChildProcessSpawner } from "effect/unstable/process"; -import { ServerConfig } from "../../config.ts"; -import { layer as idAllocatorLayer, IdAllocatorV2 } from "../IdAllocator.ts"; -import { makeLayerEffect as makeProviderAdapterRegistryLayerEffect } from "../ProviderAdapterRegistry.ts"; +import * as ServerConfig from "../../config.ts"; +import * as IdAllocator from "../IdAllocator.ts"; +import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; import type { ProviderReplayGate } from "../testkit/ProviderReplayGate.testkit.ts"; import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts"; import { makeReplayServerConfig } from "../testkit/ProviderReplayHarness.ts"; @@ -38,18 +38,18 @@ function makeAcpRegistryProviderAdapterRegistryReplayLayer( options: { readonly replayGate?: ProviderReplayGate } = {}, ) { const serverConfigLayer = Layer.effect( - ServerConfig, + ServerConfig.ServerConfig, makeReplayServerConfig(`acp-registry-${transcript.scenario}`).pipe(Effect.orDie), ).pipe(Layer.provide(NodeServices.layer)); - return makeProviderAdapterRegistryLayerEffect( + return ProviderAdapterRegistry.makeLayerEffect( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const crypto = yield* Crypto.Crypto; - const idAllocator = yield* IdAllocatorV2; - const serverConfig = yield* ServerConfig; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const serverConfig = yield* ServerConfig.ServerConfig; const replayGate = options.replayGate; const replayDir = yield* fileSystem .makeTempDirectory({ @@ -86,7 +86,7 @@ function makeAcpRegistryProviderAdapterRegistryReplayLayer( return [adapter]; }), ).pipe( - Layer.provide(Layer.mergeAll(serverConfigLayer, NodeServices.layer, idAllocatorLayer)), + Layer.provide(Layer.mergeAll(serverConfigLayer, NodeServices.layer, IdAllocator.layer)), // Held inbound lines must not outlive the scenario and wedge teardown. Layer.merge( Layer.effectDiscard( diff --git a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.ts index 4c1b9b476ecd..76326b274528 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.ts @@ -21,19 +21,19 @@ import type * as Scope from "effect/Scope"; import { ChildProcessSpawner } from "effect/unstable/process"; import * as EffectAcpErrors from "effect-acp/errors"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import { normalizeAcpRegistryCommands, normalizeAcpRegistryLiveConfiguration, normalizeAcpRegistryWebUrl, } from "../../provider/acp/AcpRegistryProbe.ts"; -import { AcpRegistryCatalog } 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 AcpSessionRuntime from "../../provider/acp/AcpSessionRuntime.ts"; import { makeAcpNativeLoggerFactory } from "../../provider/acp/AcpNativeLogging.ts"; -import { ProviderEventLoggers } from "../../provider/Layers/ProviderEventLoggers.ts"; +import * as ProviderEventLoggers from "../../provider/Layers/ProviderEventLoggers.ts"; import { mergeProviderInstanceEnvironment } from "../../provider/ProviderInstanceEnvironment.ts"; -import { IdAllocatorV2 } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { makeProviderFailure } from "../ProviderFailure.ts"; import { ProviderAdapterDriverCreateError, @@ -62,10 +62,10 @@ export interface AcpRegistryAdapterV2Options { readonly crypto: Crypto.Crypto; readonly selfInvocation: SelfInvocation; readonly fileSystem: FileSystem.FileSystem; - readonly idAllocator: IdAllocatorV2["Service"]; - readonly resolver: Pick; - readonly runtimeCoordinator?: AcpRegistryRuntimeCoordinator["Service"]; - readonly serverConfig: ServerConfig["Service"]; + readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; + readonly resolver: Pick; + readonly runtimeCoordinator?: AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator["Service"]; + readonly serverConfig: ServerConfig.ServerConfig["Service"]; readonly nativeLogging?: Parameters[0]["nativeLogging"]; readonly makeRuntime?: ( input: AcpAdapterV2RuntimeInput, @@ -262,11 +262,11 @@ export type AcpRegistryAdapterV2DriverEnv = | ChildProcessSpawner.ChildProcessSpawner | Crypto.Crypto | FileSystem.FileSystem - | AcpRegistryCatalog - | IdAllocatorV2 + | AcpRegistrySupport.AcpRegistryCatalog + | IdAllocator.IdAllocatorV2 | Path.Path - | ProviderEventLoggers - | ServerConfig; + | ProviderEventLoggers.ProviderEventLoggers + | ServerConfig.ServerConfig; export const AcpRegistryAdapterV2Driver: ProviderAdapterDriver< AcpRegistrySettings, @@ -282,12 +282,14 @@ export const AcpRegistryAdapterV2Driver: ProviderAdapterDriver< const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const crypto = yield* Crypto.Crypto; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; - const providerEventLoggers = yield* ProviderEventLoggers; - const serverConfig = yield* ServerConfig; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const providerEventLoggers = yield* ProviderEventLoggers.ProviderEventLoggers; + const serverConfig = yield* ServerConfig.ServerConfig; const makeNativeLogger = yield* makeAcpNativeLoggerFactory(); - const resolver = yield* AcpRegistryCatalog; - const runtimeCoordinator = yield* Effect.serviceOption(AcpRegistryRuntimeCoordinator); + const resolver = yield* AcpRegistrySupport.AcpRegistryCatalog; + const runtimeCoordinator = yield* Effect.serviceOption( + AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator, + ); return makeAcpRegistryAdapterV2({ instanceId: input.instanceId, settings: { ...input.config, enabled: input.enabled }, diff --git a/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts index 2688012e50a9..6f2924c86bfc 100644 --- a/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts @@ -22,10 +22,10 @@ import * as Stream from "effect/Stream"; import { ChildProcessSpawner } from "effect/unstable/process"; import type * as EffectAcpSchema from "effect-acp/compat"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import type * as AcpSessionRuntime from "../../provider/acp/AcpSessionRuntime.ts"; import { makeAntigravityAcpRuntime } from "../../provider/acp/AntigravityAcpSupport.ts"; -import { layer as idAllocatorLayer, IdAllocatorV2 } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { ProviderAdapterV2RuntimePolicy } from "../ProviderAdapter.ts"; import { makeAntigravityAcpAdapterFlavor, @@ -128,7 +128,7 @@ describe("AntigravityAdapterV2 flavor", () => { const sessionLayer = Layer.mergeAll( NodeServices.layer, - idAllocatorLayer, + IdAllocator.layer, ServerConfig.layerTest(process.cwd(), { prefix: "t3-antigravity-v2-adapter-" }).pipe( Layer.provide(NodeServices.layer), ), @@ -140,7 +140,7 @@ describe("AntigravityAdapterV2 client file system", () => { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), ); @@ -156,7 +156,7 @@ describe("AntigravityAdapterV2 client file system", () => { selfInvocation: yield* resolveSelfInvocation(), fileSystem, path, - idAllocator: yield* IdAllocatorV2, + idAllocator: yield* IdAllocator.IdAllocatorV2, serverConfig, makeRuntime: (input) => makeAntigravityAcpRuntime({ @@ -294,7 +294,7 @@ describe("AntigravityAdapterV2 workspace changes", () => { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const crypto = yield* Crypto.Crypto; const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -308,7 +308,7 @@ describe("AntigravityAdapterV2 workspace changes", () => { selfInvocation: yield* resolveSelfInvocation(), fileSystem, path, - idAllocator: yield* IdAllocatorV2, + idAllocator: yield* IdAllocator.IdAllocatorV2, serverConfig, makeRuntime: (input) => makeAntigravityAcpRuntime({ @@ -431,7 +431,7 @@ describe("AntigravityAdapterV2 client file system under restrictive policies", ( const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; - const serverConfig = yield* ServerConfig; + const serverConfig = yield* ServerConfig.ServerConfig; const crypto = yield* Crypto.Crypto; const mockAgentPath = yield* path.fromFileUrl( new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), @@ -458,7 +458,7 @@ describe("AntigravityAdapterV2 client file system under restrictive policies", ( selfInvocation: yield* resolveSelfInvocation(), fileSystem, path, - idAllocator: yield* IdAllocatorV2, + idAllocator: yield* IdAllocator.IdAllocatorV2, serverConfig, makeRuntime: (input) => makeAntigravityAcpRuntime({ diff --git a/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.ts index 9c3289cff7fe..bcee0edf38c2 100644 --- a/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.ts @@ -34,7 +34,6 @@ import { makeAntigravityUserInputResponse, normalizeAntigravityToolCall, } from "../../provider/acp/AntigravityProtocol.ts"; -import type { AcpSessionRuntimeStartResult } from "../../provider/acp/AcpSessionRuntime.ts"; import type { IdAllocatorV2 } from "../IdAllocator.ts"; import { AcpProviderCapabilitiesV2, @@ -83,7 +82,7 @@ export interface AntigravityAdapterV2Options { /** Model to select for the provider default alias, from the manifest. */ readonly defaultModel: Effect.Effect; readonly onSessionStarted?: ( - started: AcpSessionRuntimeStartResult, + started: AcpSessionRuntime.AcpSessionRuntimeStartResult, cwd: string, ) => Effect.Effect; readonly onSessionEvent?: AcpAdapterV2Flavor["onSessionEvent"]; diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts index 2ef8fcc0d7c4..29c96447e9f4 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts @@ -47,7 +47,7 @@ import { Tool } from "effect/unstable/ai"; import { formatClaudeResumeCompactionQuestion } from "@t3tools/shared/claudeCompaction"; import { attachmentRelativePath } from "../../attachmentStore.ts"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import * as McpProviderSession from "../../mcp/McpProviderSession.ts"; import { PreviewControlsToolkit } from "../../mcp/toolkits/previewControls/tools.ts"; import { EnvironmentToolkit } from "../../mcp/toolkits/environment/tools.ts"; @@ -62,44 +62,15 @@ import { type ProviderAdapterV2TurnInput, } from "../ProviderAdapter.ts"; import type { ProviderContinuationRequest } from "../ProviderContinuationRequests.ts"; -import { - CLAUDE_AGENT_SDK_QUERY_PROTOCOL, - CLAUDE_DEFAULT_INSTANCE_ID, - CLAUDE_PROVIDER, - CLAUDE_READ_ONLY_ALLOWED_TOOLS, - CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS, - CLAUDE_T3_MCP_TOOL_WILDCARD, - ClaudeProviderCapabilitiesV2, - ClaudeAgentSdkQueryRunner, - ClaudeAgentSdkQueryRunnerError, - claudeEffectiveQueryPolicyKey, - claudePromptUuid, - claudeProviderTurnTokenUsage, - claudeMcpQueryOverrides, - claudeQueryMessages, - claudeRuntimeQueryPolicyForRuntimePolicy, - claudeSdkUserInputAnswers, - claudeUserInputQuestions, - claudeTodoSteps, - claudeProposedPlan, - awaitClaudeApprovalDecision, - createClaudeAdapterV2, - loggedClaudeQueryOptions, - makeClaudeAdapterV2, - makeClaudeAgentSdkProtocolLogger, - makeClaudeQueryOptions, - permissionResultFromDecision, - type ClaudeAgentSdkQueryOptions, - type ClaudeAgentSdkQueryOpenInput, -} from "./ClaudeAdapterV2.ts"; -import { layer as idAllocatorLayer, IdAllocatorV2 } from "../IdAllocator.ts"; +import * as ClaudeAdapterV2 from "./ClaudeAdapterV2.ts"; +import * as IdAllocator from "../IdAllocator.ts"; const DEFAULT_CLAUDE_SETTINGS = Schema.decodeSync(ClaudeSettings)({}); const AUTO_COMPACT_CLAUDE_SETTINGS = Schema.decodeSync(ClaudeSettings)({ autoCompactWindow: "300000", }); const CLAUDE_TEST_MODEL_SELECTION = { - instanceId: ProviderInstanceId.make(CLAUDE_PROVIDER), + instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER), model: "claude-sonnet-4-6", options: [{ id: "effort", value: "ultrathink" }], } satisfies ModelSelection; @@ -120,7 +91,7 @@ function makeClaudeTestAppThread(input: { id: input.threadId, projectId: ProjectId.make(`project-${input.threadId}`), title: "Claude attachment test", - providerInstanceId: ProviderInstanceId.make(CLAUDE_PROVIDER), + providerInstanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER), modelSelection: CLAUDE_TEST_MODEL_SELECTION, runtimeMode: "full-access", interactionMode: "default", @@ -179,7 +150,7 @@ function makeClaudeTestTurnInput(input: { describe("ClaudeAdapterV2 runtime query policy", () => { it.each([false, true])("requests thinking summaries with resume=%s", (resume) => { - const options = makeClaudeQueryOptions({ + const options = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: CLAUDE_TEST_MODEL_SELECTION, nativeThreadId: "thinking-thread", resume, @@ -191,7 +162,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { }); it("preserves an explicit omitted thinking display", () => { - const options = makeClaudeQueryOptions({ + const options = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: CLAUDE_TEST_MODEL_SELECTION, nativeThreadId: "thinking-thread", resume: false, @@ -204,7 +175,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { }); it("does not enable thinking when the model option disables it", () => { - const options = makeClaudeQueryOptions({ + const options = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: { ...CLAUDE_TEST_MODEL_SELECTION, model: "claude-haiku-4-5", @@ -224,7 +195,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { ["--dangerously-skip-permissions", "bypassPermissions"], ["--dangerously-skip-permissions --permission-mode plan", "plan"], ])("folds %s into the SDK permission mode", (launchArgs, expected) => { - const options = makeClaudeQueryOptions({ + const options = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: CLAUDE_TEST_MODEL_SELECTION, nativeThreadId: "native-permission-override", resume: false, @@ -242,7 +213,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { behavior: "completed" as const, result: "continue" as const, }); - const options = makeClaudeQueryOptions({ + const options = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: CLAUDE_TEST_MODEL_SELECTION, nativeThreadId: "native-thread-auto-compact", resume: true, @@ -259,7 +230,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { it("projects AskUserQuestion input with question text as the answer key", () => { assert.deepEqual( - claudeUserInputQuestions({ + ClaudeAdapterV2.claudeUserInputQuestions({ questions: [ { header: "Approach", @@ -279,20 +250,23 @@ describe("ClaudeAdapterV2 runtime query policy", () => { }, ], ); - const sdkAnswers: NonNullable = claudeSdkUserInputAnswers({ - "Which approach?": ["Simple", "Safe"], - "Deploy now?": "Yes", - }); + const sdkAnswers: NonNullable = + ClaudeAdapterV2.claudeSdkUserInputAnswers({ + "Which approach?": ["Simple", "Safe"], + "Deploy now?": "Yes", + }); assert.deepEqual(sdkAnswers, { "Which approach?": "Simple, Safe", "Deploy now?": "Yes", }); - assert.isTrue(ClaudeProviderCapabilitiesV2.planning.supportsStructuredQuestions); + assert.isTrue( + ClaudeAdapterV2.ClaudeProviderCapabilitiesV2.planning.supportsStructuredQuestions, + ); }); it("normalizes Claude todo and proposed-plan tool input", () => { assert.deepEqual( - claudeTodoSteps({ + ClaudeAdapterV2.claudeTodoSteps({ todos: [ { content: "Inspect", status: "completed" }, { content: "Implement", status: "in_progress" }, @@ -303,13 +277,16 @@ describe("ClaudeAdapterV2 runtime query policy", () => { { id: "todo-1", text: "Implement", status: "running" }, ], ); - assert.equal(claudeProposedPlan({ plan: " # Plan\nShip it " }), "# Plan\nShip it"); - assert.isTrue(ClaudeProviderCapabilitiesV2.planning.emitsTodoList); - assert.isTrue(ClaudeProviderCapabilitiesV2.planning.emitsProposedPlan); + assert.equal( + ClaudeAdapterV2.claudeProposedPlan({ plan: " # Plan\nShip it " }), + "# Plan\nShip it", + ); + assert.isTrue(ClaudeAdapterV2.ClaudeProviderCapabilitiesV2.planning.emitsTodoList); + assert.isTrue(ClaudeAdapterV2.ClaudeProviderCapabilitiesV2.planning.emitsProposedPlan); }); it("maps canonical read-only never policy to Claude dontAsk with read-only tools", () => { - const queryPolicy = claudeRuntimeQueryPolicyForRuntimePolicy( + const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( ProviderAdapterV2RuntimePolicy.make({ runtimeMode: "full-access", interactionMode: "default", @@ -325,14 +302,14 @@ describe("ClaudeAdapterV2 runtime query policy", () => { assert.deepEqual(queryPolicy, { permissionMode: "dontAsk", - tools: CLAUDE_READ_ONLY_ALLOWED_TOOLS, - allowedTools: CLAUDE_READ_ONLY_ALLOWED_TOOLS, + tools: ClaudeAdapterV2.CLAUDE_READ_ONLY_ALLOWED_TOOLS, + allowedTools: ClaudeAdapterV2.CLAUDE_READ_ONLY_ALLOWED_TOOLS, installPermissionCallback: false, }); }); it("maps canonical read-only on-request policy to Claude default with callbacks", () => { - const queryPolicy = claudeRuntimeQueryPolicyForRuntimePolicy( + const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( ProviderAdapterV2RuntimePolicy.make({ runtimeMode: "full-access", interactionMode: "default", @@ -348,14 +325,14 @@ describe("ClaudeAdapterV2 runtime query policy", () => { assert.deepEqual(queryPolicy, { permissionMode: "default", - tools: CLAUDE_READ_ONLY_ALLOWED_TOOLS, - allowedTools: CLAUDE_READ_ONLY_ALLOWED_TOOLS, + tools: ClaudeAdapterV2.CLAUDE_READ_ONLY_ALLOWED_TOOLS, + allowedTools: ClaudeAdapterV2.CLAUDE_READ_ONLY_ALLOWED_TOOLS, installPermissionCallback: true, }); }); it("does not auto-allow reads for canonical restricted read-only never policy", () => { - const queryPolicy = claudeRuntimeQueryPolicyForRuntimePolicy( + const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( ProviderAdapterV2RuntimePolicy.make({ runtimeMode: "full-access", interactionMode: "default", @@ -375,13 +352,13 @@ describe("ClaudeAdapterV2 runtime query policy", () => { assert.deepEqual(queryPolicy, { permissionMode: "dontAsk", - tools: CLAUDE_READ_ONLY_ALLOWED_TOOLS, + tools: ClaudeAdapterV2.CLAUDE_READ_ONLY_ALLOWED_TOOLS, installPermissionCallback: false, }); }); it("maps default full-access policy to Claude bypass permissions", () => { - const queryPolicy = claudeRuntimeQueryPolicyForRuntimePolicy( + const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( ProviderAdapterV2RuntimePolicy.make({ runtimeMode: "full-access", interactionMode: "default", @@ -397,7 +374,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { }); it("maps Auto runtime mode to Claude's AI-reviewed permission mode", () => { - const queryPolicy = claudeRuntimeQueryPolicyForRuntimePolicy( + const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( ProviderAdapterV2RuntimePolicy.make({ runtimeMode: "auto", interactionMode: "default", @@ -412,7 +389,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { }); it("keeps approval-required mode interactive with danger-full-access sandboxing", () => { - const queryPolicy = claudeRuntimeQueryPolicyForRuntimePolicy( + const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( ProviderAdapterV2RuntimePolicy.make({ runtimeMode: "approval-required", interactionMode: "default", @@ -430,7 +407,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { }); it("installs the permission callback for approval-required plan mode", () => { - const queryPolicy = claudeRuntimeQueryPolicyForRuntimePolicy( + const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( ProviderAdapterV2RuntimePolicy.make({ runtimeMode: "approval-required", interactionMode: "plan", @@ -445,7 +422,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { }); it("honors never approvals for approval-required workspace-write policy", () => { - const queryPolicy = claudeRuntimeQueryPolicyForRuntimePolicy( + const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( ProviderAdapterV2RuntimePolicy.make({ runtimeMode: "approval-required", interactionMode: "default", @@ -464,7 +441,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { }); it("honors never approvals for externally sandboxed policy", () => { - const queryPolicy = claudeRuntimeQueryPolicyForRuntimePolicy( + const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( ProviderAdapterV2RuntimePolicy.make({ runtimeMode: "approval-required", interactionMode: "default", @@ -513,7 +490,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { }; it("leaves an absent allowlist absent when no MCP session exists", () => { - const overrides = claudeMcpQueryOverrides({ + const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ threadId: ThreadId.make("thread-claude-no-mcp-no-allowlist"), readOnlySandbox: false, }); @@ -522,7 +499,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { }); it("preserves an explicit allowlist when no MCP session exists", () => { - const overrides = claudeMcpQueryOverrides({ + const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ threadId: ThreadId.make("thread-claude-no-mcp-with-allowlist"), readOnlySandbox: false, allowedTools: ["Read"], @@ -534,10 +511,13 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { it("pre-approves all t3-code tools when attaching an MCP session without an allowlist", () => { const threadId = ThreadId.make("thread-claude-mcp-no-allowlist"); withMcpSession(threadId, () => { - const overrides = claudeMcpQueryOverrides({ threadId, readOnlySandbox: false }); + const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ + threadId, + readOnlySandbox: false, + }); assert.deepEqual(overrides, { - allowedTools: [CLAUDE_T3_MCP_TOOL_WILDCARD], + allowedTools: [ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_WILDCARD], mcpServers: T3_MCP_SERVERS, }); }); @@ -546,7 +526,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { it("extends an explicit allowlist with the t3-code wildcard", () => { const threadId = ThreadId.make("thread-claude-mcp-with-allowlist"); withMcpSession(threadId, () => { - const overrides = claudeMcpQueryOverrides({ + const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ threadId, readOnlySandbox: false, allowedTools: ["Read", "mcp__t3-code__*"], @@ -562,33 +542,41 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { it("pre-approves only read-only t3-code tools in a read-only sandbox", () => { const threadId = ThreadId.make("thread-claude-mcp-read-only"); withMcpSession(threadId, () => { - const overrides = claudeMcpQueryOverrides({ + const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ threadId, readOnlySandbox: true, - allowedTools: [...CLAUDE_READ_ONLY_ALLOWED_TOOLS], + allowedTools: [...ClaudeAdapterV2.CLAUDE_READ_ONLY_ALLOWED_TOOLS], }); assert.deepEqual(overrides, { - allowedTools: [...CLAUDE_READ_ONLY_ALLOWED_TOOLS, ...CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS], + allowedTools: [ + ...ClaudeAdapterV2.CLAUDE_READ_ONLY_ALLOWED_TOOLS, + ...ClaudeAdapterV2.CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS, + ], mcpServers: T3_MCP_SERVERS, }); - assert.isFalse(overrides.allowedTools?.includes(CLAUDE_T3_MCP_TOOL_WILDCARD)); + assert.isFalse(overrides.allowedTools?.includes(ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_WILDCARD)); }); }); it("pre-approves only read-only t3-code tools in a read-only sandbox without an allowlist", () => { const threadId = ThreadId.make("thread-claude-mcp-read-only-no-allowlist"); withMcpSession(threadId, () => { - const overrides = claudeMcpQueryOverrides({ threadId, readOnlySandbox: true }); + const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ + threadId, + readOnlySandbox: true, + }); - assert.deepEqual(overrides.allowedTools, [...CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS]); + assert.deepEqual(overrides.allowedTools, [ + ...ClaudeAdapterV2.CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS, + ]); }); }); it("keys live-query reuse on the MCP-derived pre-approvals", () => { const threadId = ThreadId.make("thread-claude-mcp-query-key"); withMcpSession(threadId, () => { - const queryPolicy = claudeRuntimeQueryPolicyForRuntimePolicy( + const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( ProviderAdapterV2RuntimePolicy.make({ runtimeMode: "full-access", interactionMode: "default", @@ -602,15 +590,15 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { }), ); - const readOnlyKey = claudeEffectiveQueryPolicyKey( + const readOnlyKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey( queryPolicy, - claudeMcpQueryOverrides({ threadId, readOnlySandbox: true }), + ClaudeAdapterV2.claudeMcpQueryOverrides({ threadId, readOnlySandbox: true }), ); - const fullAccessKey = claudeEffectiveQueryPolicyKey( + const fullAccessKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey( queryPolicy, - claudeMcpQueryOverrides({ threadId, readOnlySandbox: false }), + ClaudeAdapterV2.claudeMcpQueryOverrides({ threadId, readOnlySandbox: false }), ); - const detachedKey = claudeEffectiveQueryPolicyKey(queryPolicy, {}); + const detachedKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey(queryPolicy, {}); assert.notEqual(readOnlyKey, fullAccessKey); assert.notEqual(fullAccessKey, detachedKey); @@ -620,16 +608,16 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { it("invalidates live-query reuse when MCP credentials rotate", () => { const threadId = ThreadId.make("thread-claude-mcp-credential-rotation"); withMcpSession(threadId, () => { - const queryPolicy = claudeRuntimeQueryPolicyForRuntimePolicy( + const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( ProviderAdapterV2RuntimePolicy.make({ runtimeMode: "approval-required", interactionMode: "default", cwd: "/workspace", }), ); - const initialKey = claudeEffectiveQueryPolicyKey( + const initialKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey( queryPolicy, - claudeMcpQueryOverrides({ threadId, readOnlySandbox: false }), + ClaudeAdapterV2.claudeMcpQueryOverrides({ threadId, readOnlySandbox: false }), ); McpProviderSession.setMcpProviderSession({ @@ -642,9 +630,9 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { browserToolsAvailable: true, }); - const rotatedKey = claudeEffectiveQueryPolicyKey( + const rotatedKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey( queryPolicy, - claudeMcpQueryOverrides({ threadId, readOnlySandbox: false }), + ClaudeAdapterV2.claudeMcpQueryOverrides({ threadId, readOnlySandbox: false }), ); assert.notEqual(rotatedKey, initialKey); }); @@ -663,7 +651,10 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { .map((tool) => `mcp__t3-code__${tool.name}`) .sort(); - assert.deepEqual([...CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS].sort(), readOnlyToolNames); + assert.deepEqual( + [...ClaudeAdapterV2.CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS].sort(), + readOnlyToolNames, + ); }); }); @@ -681,7 +672,7 @@ describe("ClaudeAdapterV2 native protocol logging", () => { }); try { - const overrides = claudeMcpQueryOverrides({ + const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ threadId, readOnlySandbox: false, allowedTools: ["Read"], @@ -699,7 +690,7 @@ describe("ClaudeAdapterV2 native protocol logging", () => { }, }); - const options = makeClaudeQueryOptions({ + const options = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: { instanceId: ProviderInstanceId.make("claudeAgent"), model: "claude-sonnet-4-6", @@ -718,7 +709,7 @@ describe("ClaudeAdapterV2 native protocol logging", () => { assert.equal(systemPrompt.type, "preset"); assert.equal(systemPrompt.preset, "claude_code"); assert.include(systemPrompt.append ?? "", "Use `delegate_task`"); - const logged = loggedClaudeQueryOptions(options); + const logged = ClaudeAdapterV2.loggedClaudeQueryOptions(options); assert.equal(logged.hasMcpServers, true); assert.notInclude(JSON.stringify(logged), "secret-claude-token"); } finally { @@ -742,7 +733,7 @@ describe("ClaudeAdapterV2 native protocol logging", () => { }; const threadId = ThreadId.make("thread-1"); const providerSessionId = ProviderSessionId.make("provider-session-1"); - const protocolLogger = makeClaudeAgentSdkProtocolLogger({ + const protocolLogger = ClaudeAdapterV2.makeClaudeAgentSdkProtocolLogger({ nativeEventLogger: logger, threadId, providerSessionId, @@ -785,7 +776,7 @@ describe("ClaudeAdapterV2 native protocol logging", () => { assert.equal(writes[0]?.threadId, threadId); assert.deepEqual(writes[0]?.event, { provider: "claudeAgent", - protocol: CLAUDE_AGENT_SDK_QUERY_PROTOCOL, + protocol: ClaudeAdapterV2.CLAUDE_AGENT_SDK_QUERY_PROTOCOL, kind: "protocol", providerSessionId, event: { @@ -800,7 +791,7 @@ describe("ClaudeAdapterV2 native protocol logging", () => { ); it("logs query options without leaking environment values or callback functions", () => { - const options: ClaudeAgentSdkQueryOptions = { + const options: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions = { model: "claude-sonnet-4-6", tools: { type: "preset", @@ -824,7 +815,7 @@ describe("ClaudeAdapterV2 native protocol logging", () => { }), }; - assert.deepEqual(loggedClaudeQueryOptions(options), { + assert.deepEqual(ClaudeAdapterV2.loggedClaudeQueryOptions(options), { model: "claude-sonnet-4-6", tools: { type: "preset", @@ -837,13 +828,16 @@ describe("ClaudeAdapterV2 native protocol logging", () => { hasEnvironment: true, hasExtraArgs: true, }); - assert.notInclude(JSON.stringify(loggedClaudeQueryOptions(options)), "secret launch prompt"); + assert.notInclude( + JSON.stringify(ClaudeAdapterV2.loggedClaudeQueryOptions(options)), + "secret launch prompt", + ); }); }); describe("ClaudeAdapterV2 context usage", () => { it("projects assistant usage against the selected context window", () => { - const usage = claudeProviderTurnTokenUsage( + const usage = ClaudeAdapterV2.claudeProviderTurnTokenUsage( { input_tokens: 42_000, cache_creation_input_tokens: 2_000, @@ -868,7 +862,7 @@ describe("ClaudeAdapterV2 context usage", () => { describe("ClaudeAdapterV2 session permissions", () => { it("forces suggested permission updates to session scope", () => { - const result = permissionResultFromDecision({ + const result = ClaudeAdapterV2.permissionResultFromDecision({ toolName: "Bash", decision: "acceptForSession", toolInput: { command: "git status" }, @@ -898,7 +892,7 @@ describe("ClaudeAdapterV2 session permissions", () => { }); it("adds a whole-tool session rule when Claude offers no suggestion", () => { - const result = permissionResultFromDecision({ + const result = ClaudeAdapterV2.permissionResultFromDecision({ toolName: "mcp__t3__custom_tool", decision: "acceptForSession", toolInput: {}, @@ -925,13 +919,13 @@ describe("ClaudeAdapterV2 Auto-accept edits", () => { Effect.scoped( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-accept-edits-", }); - let openedOptions: ClaudeAgentSdkQueryOptions | undefined; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + let openedOptions: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions | undefined; + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: {}, attachmentsDir, @@ -1023,7 +1017,7 @@ describe("ClaudeAdapterV2 Auto-accept edits", () => { decision: "accept", }); assert.equal((yield* Fiber.join(decision))?.behavior, "allow"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); }); @@ -1035,7 +1029,10 @@ describe("ClaudeAdapterV2 approval cancellation", () => { const controller = new AbortController(); controller.abort(); - const result = yield* awaitClaudeApprovalDecision(decision, controller.signal); + const result = yield* ClaudeAdapterV2.awaitClaudeApprovalDecision( + decision, + controller.signal, + ); assert.equal(result, "cancel"); }), @@ -1052,7 +1049,7 @@ describe("ClaudeAdapterV2 approval cancellation", () => { return removeEventListener(...args); }; const fiber = yield* Effect.forkChild( - awaitClaudeApprovalDecision(decision, controller.signal), + ClaudeAdapterV2.awaitClaudeApprovalDecision(decision, controller.signal), ); yield* Effect.yieldNow; yield* Deferred.succeed(decision, "accept"); @@ -1070,9 +1067,9 @@ describe("ClaudeAdapterV2 executable path", () => { Effect.gen(function* () { const path = yield* Path.Path; const executablePaths: Array = []; - const adapter = yield* createClaudeAdapterV2( + const adapter = yield* ClaudeAdapterV2.createClaudeAdapterV2( { - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, displayName: undefined, environment: [], enabled: true, @@ -1081,9 +1078,11 @@ describe("ClaudeAdapterV2 executable path", () => { {}, ).pipe( Effect.provide( - ServerConfig.layerTest(process.cwd(), { prefix: "t3-claude-binary-home-" }), + ServerConfig.layerTest(process.cwd(), { + prefix: "t3-claude-binary-home-", + }), ), - Effect.provideService(ClaudeAgentSdkQueryRunner, { + Effect.provideService(ClaudeAdapterV2.ClaudeAgentSdkQueryRunner, { allocateSessionId: Effect.succeed("native-thread-claude-binary-home"), open: (input) => Effect.sync(() => { @@ -1126,7 +1125,7 @@ describe("ClaudeAdapterV2 executable path", () => { assert.deepEqual(executablePaths, [path.join(NodeOS.homedir(), "bin", "claude")]); }), - ).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + ).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); }); @@ -1135,13 +1134,13 @@ describe("ClaudeAdapterV2 resume compaction", () => { Effect.scoped( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-resume-", }); - let openedOptions: ClaudeAgentSdkQueryOptions | undefined; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + let openedOptions: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions | undefined; + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: {}, attachmentsDir, @@ -1342,7 +1341,7 @@ describe("ClaudeAdapterV2 resume compaction", () => { ), { behavior: "cancelled" }, ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); }); @@ -1352,14 +1351,14 @@ describe("ClaudeAdapterV2 attachments", () => { Effect.scoped( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const path = yield* Path.Path; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-v2-attachments-", }); const offeredMessages: Array = []; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: {}, attachmentsDir, @@ -1459,7 +1458,7 @@ describe("ClaudeAdapterV2 attachments", () => { ]); const providerTurnId = idAllocator.derive.providerTurn({ - driver: CLAUDE_PROVIDER, + driver: ClaudeAdapterV2.CLAUDE_PROVIDER, nativeTurnId: `turn:${attemptId}`, }); yield* runtime.steerTurn({ @@ -1484,7 +1483,7 @@ describe("ClaudeAdapterV2 attachments", () => { text: `Ultrathink:\nFocus on the diagram labels.\n\n[Attached image "diagram.png" is saved at: ${expectedAttachmentPath}]\n\n[Attached file "requirements.pdf" is saved at: ${expectedDocumentPath}]`, }, ]); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -1492,13 +1491,13 @@ describe("ClaudeAdapterV2 attachments", () => { Effect.scoped( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-v2-unsupported-attachment-", }); let openCount = 0; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: {}, attachmentsDir, @@ -1564,7 +1563,7 @@ describe("ClaudeAdapterV2 attachments", () => { assert.equal(error._tag, "ProviderAdapterTurnStartError"); assert.include(String(error.cause), "Unsupported Claude image attachment type"); assert.equal(openCount, 0); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); }); @@ -1574,19 +1573,19 @@ describe("ClaudeAdapterV2 native fork", () => { Effect.scoped( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-v2-fork-attachments-", }); - const openedQueries: Array = []; + const openedQueries: Array = []; const forkCalls: Array<{ readonly sessionId: string; readonly options: unknown; readonly threadId: ThreadId; readonly providerSessionId: ProviderSessionId; }> = []; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: {}, attachmentsDir, @@ -1622,7 +1621,7 @@ describe("ClaudeAdapterV2 native fork", () => { threadId: sourceThreadId, providerSessionId, modelSelection: { - instanceId: ProviderInstanceId.make(CLAUDE_PROVIDER), + instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER), model: "claude-sonnet-4-6", }, runtimePolicy: ProviderAdapterV2RuntimePolicy.make({ @@ -1634,7 +1633,7 @@ describe("ClaudeAdapterV2 native fork", () => { const sourceProviderThread = yield* runtime.ensureThread({ threadId: sourceThreadId, modelSelection: { - instanceId: ProviderInstanceId.make(CLAUDE_PROVIDER), + instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER), model: "claude-sonnet-4-6", }, runtimePolicy: ProviderAdapterV2RuntimePolicy.make({ @@ -1654,7 +1653,7 @@ describe("ClaudeAdapterV2 native fork", () => { nodeId: NodeId.make("node-claude-source"), runAttemptId: RunAttemptId.make("run-attempt-claude-source"), nativeTurnRef: { - driver: CLAUDE_PROVIDER, + driver: ClaudeAdapterV2.CLAUDE_PROVIDER, nativeId: "assistant-message-cursor", strength: "weak", }, @@ -1690,9 +1689,9 @@ describe("ClaudeAdapterV2 native fork", () => { id: targetThreadId, projectId: ProjectId.make("project-claude-fork-target"), title: "Claude fork target", - providerInstanceId: ProviderInstanceId.make(CLAUDE_PROVIDER), + providerInstanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER), modelSelection: { - instanceId: ProviderInstanceId.make(CLAUDE_PROVIDER), + instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER), model: "claude-sonnet-4-6", }, runtimeMode: "full-access", @@ -1729,7 +1728,7 @@ describe("ClaudeAdapterV2 native fork", () => { attachments: [], }, modelSelection: { - instanceId: ProviderInstanceId.make(CLAUDE_PROVIDER), + instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER), model: "claude-sonnet-4-6", }, runtimePolicy: ProviderAdapterV2RuntimePolicy.make({ @@ -1741,7 +1740,7 @@ describe("ClaudeAdapterV2 native fork", () => { assert.equal(openedQueries[0]?.options.resume, "forked-native-session"); assert.equal(openedQueries[0]?.options.sessionId, undefined); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); }); @@ -1751,13 +1750,13 @@ describe("ClaudeAdapterV2 native session identity", () => { Effect.scoped( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-v2-session-identity-", }); - const openedQueries: Array = []; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + const openedQueries: Array = []; + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: {}, attachmentsDir, @@ -1808,7 +1807,7 @@ describe("ClaudeAdapterV2 native session identity", () => { }), ); return openedQueries; - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect("creates the native session on the first provider turn", () => @@ -2008,7 +2007,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { }) => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-v2-wake-", }); @@ -2026,9 +2025,9 @@ describe("ClaudeAdapterV2 background wake turns", () => { yield* Queue.unbounded>(); const systemNoticeReceipts = yield* Queue.unbounded>(); - let openedOptions: ClaudeAgentSdkQueryOptions | undefined; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + let openedOptions: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions | undefined; + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: options?.environment ?? {}, attachmentsDir, @@ -2247,7 +2246,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ), ); for (const item of latest.values()) assert.notInclude(item.text, "secret-signature"); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); for (const terminalReason of ["aborted_tools", "aborted_streaming"] as const) { @@ -2256,7 +2255,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { Effect.scoped( Effect.gen(function* () { const harness = yield* makeWakeHarness; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attemptId = RunAttemptId.make("attempt-steering-abort"); const input = makeClaudeTestTurnInput({ threadId: harness.threadId, @@ -2273,7 +2272,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { runId: input.runId, providerThread: harness.providerThread, providerTurnId: idAllocator.derive.providerTurn({ - driver: CLAUDE_PROVIDER, + driver: ClaudeAdapterV2.CLAUDE_PROVIDER, nativeTurnId: `turn:${attemptId}`, }), message: { @@ -2317,7 +2316,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ); } assert.lengthOf(harness.terminalEvents(), 1); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); } @@ -2385,7 +2384,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { yield* Queue.take(harness.terminalReceipts); assert.lengthOf(notices(), 3); assert.notEqual(notices()[0]?.id, notices()[2]?.id); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect("keeps usage warnings and provisioned overage silent", () => @@ -2432,7 +2431,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { (event) => event.type === "turn_item.updated" && event.turnItem.type === "system_notice", ), ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect("names an expired Claude login instead of the terminal API error", () => @@ -2477,7 +2476,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { assert.include(terminal.failure.message, configDir); assert.include(terminal.failure.message, cwd); assert.notInclude(terminal.failure.message, "repeated API errors"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect.each([ @@ -2531,7 +2530,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { if (terminal.status !== "failed") return; assert.include(terminal.failure.message, expected); assert.equal(terminal.failure.class, recovered ? "provider_error" : "usage_limit"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect.each([ @@ -2591,7 +2590,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ? "Claude usage limit reached. Send the message again once the limit resets." : "Claude gave up after repeated API errors.", ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect.each([429, 401, 529])( @@ -2635,7 +2634,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ); if (apiErrorStatus !== 429) assert.notInclude(terminal.failure.message.toLowerCase(), "usage limit"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect("surfaces a Claude safety model fallback without failing the turn", () => @@ -2694,7 +2693,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { (event) => event.type === "turn_item.updated" && event.turnItem.type === "error", ), ); - }).pipe(Effect.scoped, Effect.provide(Layer.mergeAll(NodeServices.layer, idAllocatorLayer))), + }).pipe(Effect.scoped, Effect.provide(Layer.mergeAll(NodeServices.layer, IdAllocator.layer))), ); it.effect( @@ -2766,7 +2765,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { cacheCreationTokens: 0, }); } - }).pipe(Effect.scoped, Effect.provide(Layer.mergeAll(NodeServices.layer, idAllocatorLayer))), + }).pipe(Effect.scoped, Effect.provide(Layer.mergeAll(NodeServices.layer, IdAllocator.layer))), ); it.effect("retains image preview paths on Claude Read tool completion", () => @@ -2859,7 +2858,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ); for (const item of items.filter((item) => item.nativeItemRef?.nativeId !== "image")) assert.notProperty(item, "viewedImagePath"); - }).pipe(Effect.scoped, Effect.provide(Layer.mergeAll(NodeServices.layer, idAllocatorLayer))), + }).pipe(Effect.scoped, Effect.provide(Layer.mergeAll(NodeServices.layer, IdAllocator.layer))), ); it.effect("preserves typed Claude plans and todos through generic tool completion", () => @@ -3053,7 +3052,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ); const proposedPlan = [...plans.values()].find((plan) => plan.kind === "proposed_plan"); assert.equal(proposedPlan?.status, "active"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3127,7 +3126,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { }), ); yield* awaitUntil(() => harness.terminalEvents().length === 1, "recovered Claude turn"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3181,7 +3180,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { }); assert.isDefined(terminal.retryStartedAt); assert.equal(terminal.failure.code, "api_error_529"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3244,7 +3243,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { terminal.failure.code, terminalReason === "overloaded_status" ? "api_error_529" : terminalReason, ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); } @@ -3304,7 +3303,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { "empty roster clear", ); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3439,7 +3438,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { assert.deepEqual(roster, [ { taskId: longRunning.taskId, kind: "monitor", description: "Monitor 0" }, ]); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3489,7 +3488,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ); assert.isFalse(yield* harness.hasPendingBackgroundWork); assert.lengthOf(harness.continuationRequests, 0); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3497,7 +3496,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { Effect.scoped( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-v2-settled-stop-replaced-", }); @@ -3505,8 +3504,8 @@ describe("ClaudeAdapterV2 background wake turns", () => { const firstCloseRequested = yield* Deferred.make(); const events: Array = []; const continuationRequests: Array = []; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: {}, attachmentsDir, @@ -3668,7 +3667,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { }), ); yield* awaitUntil(() => continuationRequests.length === 1, "replacement wake"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3726,7 +3725,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { const afterFailure = providerThreadRosterEvents(harness.events).at(-1); assert.deepEqual(afterFailure?.providerThread.pendingBackgroundTasks ?? [], []); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3736,7 +3735,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { Effect.scoped( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-v2-sibling-replace-", }); @@ -3749,8 +3748,8 @@ describe("ClaudeAdapterV2 background wake turns", () => { readonly queue: Queue.Queue; }> = []; const events: Array = []; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: {}, attachmentsDir, @@ -3986,7 +3985,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { assert.isFalse(yield* hasPendingBackgroundWorkForThread(providerThreadA)); assert.isFalse(yield* hasPendingBackgroundWorkForThread(providerThreadB)); assert.isFalse(yield* hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4008,7 +4007,10 @@ describe("ClaudeAdapterV2 background wake turns", () => { const planToolUseId = "toolu_01WakePlanExitPlanMode"; const planMarkdown = "# Wake plan\n\n1. Report the background result."; const stamp = (frame: SDKMessage, attemptId: RunAttemptId) => - claudeSdkFrame({ ...frame, user_message_uuid: claudePromptUuid(attemptId) }); + claudeSdkFrame({ + ...frame, + user_message_uuid: ClaudeAdapterV2.claudePromptUuid(attemptId), + }); const runOf = (attemptId: RunAttemptId) => RunId.make(`run-${attemptId}`); const planToolUse = claudeSdkFrame({ type: "assistant", @@ -4203,7 +4205,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { userRunItems.map((item) => item.type), ["assistant_message"], ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4221,7 +4223,10 @@ describe("ClaudeAdapterV2 background wake turns", () => { const userAttempt = RunAttemptId.make("attempt-claude-wake-approval-2"); const bashToolUseId = "toolu_01WakeApprovalBash"; const stamp = (frame: SDKMessage, attemptId: RunAttemptId) => - claudeSdkFrame({ ...frame, user_message_uuid: claudePromptUuid(attemptId) }); + claudeSdkFrame({ + ...frame, + user_message_uuid: ClaudeAdapterV2.claudePromptUuid(attemptId), + }); yield* harness.runtime.startTurn( makeClaudeTestTurnInput({ @@ -4329,7 +4334,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { }); const result = yield* Fiber.join(permission); assert.equal(result?.behavior, "allow"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4341,7 +4346,10 @@ describe("ClaudeAdapterV2 background wake turns", () => { const firstAttempt = RunAttemptId.make("attempt-claude-echo-debris-1"); const secondAttempt = RunAttemptId.make("attempt-claude-echo-debris-2"); const stamp = (frame: SDKMessage, attemptId: RunAttemptId) => - claudeSdkFrame({ ...frame, user_message_uuid: claudePromptUuid(attemptId) }); + claudeSdkFrame({ + ...frame, + user_message_uuid: ClaudeAdapterV2.claudePromptUuid(attemptId), + }); yield* harness.runtime.startTurn( makeClaudeTestTurnInput({ @@ -4422,7 +4430,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { event.message.text === STALE_TASK_NOTIFICATION_RESULT_TEXT, ), ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4436,7 +4444,10 @@ describe("ClaudeAdapterV2 background wake turns", () => { const firstAttempt = RunAttemptId.make("attempt-claude-echo-1"); const secondAttempt = RunAttemptId.make("attempt-claude-echo-2"); const stamp = (frame: SDKMessage, attemptId: RunAttemptId) => - claudeSdkFrame({ ...frame, user_message_uuid: claudePromptUuid(attemptId) }); + claudeSdkFrame({ + ...frame, + user_message_uuid: ClaudeAdapterV2.claudePromptUuid(attemptId), + }); const assistantTexts = () => harness.events.flatMap((event) => event.type === "message.updated" && event.message.role === "assistant" @@ -4456,7 +4467,10 @@ describe("ClaudeAdapterV2 background wake turns", () => { attachments: [], }), ); - assert.equal(harness.offeredMessages[0]?.uuid, claudePromptUuid(firstAttempt)); + assert.equal( + harness.offeredMessages[0]?.uuid, + ClaudeAdapterV2.claudePromptUuid(firstAttempt), + ); yield* Queue.offer( harness.sdkMessages, stamp( @@ -4498,7 +4512,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { yield* awaitUntil(() => harness.terminalEvents().length === 2, "second turn terminal"); assert.deepEqual(assistantTexts(), ["One.", "Two."]); assert.equal(harness.terminalEvents()[1]?.status, "failed"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4535,7 +4549,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { yield* awaitUntil(() => harness.continuationRequests.length === 1, "continuation request"); assert.equal(harness.continuationRequests[0]?.threadId, harness.threadId); assert.equal(harness.continuationRequests[0]?.providerThreadId, harness.providerThread.id); - assert.equal(harness.continuationRequests[0]?.driver, CLAUDE_PROVIDER); + assert.equal(harness.continuationRequests[0]?.driver, ClaudeAdapterV2.CLAUDE_PROVIDER); assert.equal(harness.continuationRequests[0]?.detail, WAKE_SUMMARY); yield* Queue.offer(harness.sdkMessages, wakeResult); @@ -4544,7 +4558,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { assert.lengthOf(harness.continuationRequests, 1); assert.lengthOf(harness.terminalEvents(), 1); assert.isTrue(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4595,7 +4609,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ); assert.equal(harness.terminalEvents()[1]?.status, "completed"); assert.lengthOf(harness.offeredMessages, 1); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4683,7 +4697,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { assert.equal(pause.turnItem.type, "system_notice"); if (pause.turnItem.type !== "system_notice") return; assert.include(pause.turnItem.message, "This turn is paused until the 5-hour limit"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4774,7 +4788,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ), ); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4836,7 +4850,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ); yield* awaitUntil(() => harness.terminalEvents().length === 2, "queued turn terminal"); assert.equal(harness.terminalEvents()[1]?.status, "completed"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4874,7 +4888,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { (event) => event.type === "message.updated" && event.message.text === fallbackText, ), ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4957,7 +4971,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { assert.equal(harness.terminalEvents()[0]?.status, "completed"); assert.isTrue(hasMessageText(recoveryAssistantText)); assert.isFalse(hasMessageText(staleResultText)); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5007,7 +5021,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ); yield* awaitUntil(() => harness.terminalEvents().length === 2, "continuation terminal"); assert.equal(harness.terminalEvents()[1]?.status, "completed"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5025,11 +5039,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { Deferred.await(closeGate).pipe(Effect.andThen(Queue.shutdown(sdkMessages))), interrupt: Deferred.succeed(interruptStarted, undefined), }); - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const now = yield* DateTime.now; const attemptId = RunAttemptId.make("attempt-claude-interrupt-positive-notif"); const providerTurnId = idAllocator.derive.providerTurn({ - driver: CLAUDE_PROVIDER, + driver: ClaudeAdapterV2.CLAUDE_PROVIDER, nativeTurnId: `turn:${attemptId}`, }); @@ -5066,7 +5080,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { assert.isTrue(terminalized); assert.equal(harness.terminalEvents()[0]?.status, "interrupted"); assert.lengthOf(harness.terminalEvents(), 1); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5082,11 +5096,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { Deferred.await(closeGate).pipe(Effect.andThen(Queue.shutdown(sdkMessages))), interrupt: Deferred.succeed(interruptStarted, undefined), }); - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const now = yield* DateTime.now; const attemptId = RunAttemptId.make("attempt-claude-interrupt-zero-notif"); const providerTurnId = idAllocator.derive.providerTurn({ - driver: CLAUDE_PROVIDER, + driver: ClaudeAdapterV2.CLAUDE_PROVIDER, nativeTurnId: `turn:${attemptId}`, }); const staleText = "Zero-turn debris must not leak."; @@ -5125,7 +5139,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { (event) => event.type === "message.updated" && event.message.text === staleText, ), ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5159,7 +5173,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ); yield* awaitUntil(() => harness.terminalEvents().length === 1, "failed terminal"); assert.equal(harness.terminalEvents()[0]?.status, "failed"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5185,7 +5199,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { yield* awaitUntil(() => harness.terminalEvents().length === 1, "spurious terminal"); assert.equal(harness.terminalEvents()[0]?.status, "completed"); assert.lengthOf(harness.offeredMessages, 0); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5382,7 +5396,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { harness.continuationRequests[1]?.notification?.summary, `Command "${WAKE_TASK_DESCRIPTION}" finished`, ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5443,7 +5457,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { harness.continuationRequests[0]?.notification?.summary, `Command "${WAKE_TASK_DESCRIPTION}" finished`, ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5459,7 +5473,10 @@ describe("ClaudeAdapterV2 background wake turns", () => { const continuationAttempt = RunAttemptId.make("attempt-claude-wake-subagent-3"); const runOf = (attemptId: RunAttemptId) => RunId.make(`run-${attemptId}`); const stamp = (frame: SDKMessage, attemptId: RunAttemptId) => - claudeSdkFrame({ ...frame, user_message_uuid: claudePromptUuid(attemptId) }); + claudeSdkFrame({ + ...frame, + user_message_uuid: ClaudeAdapterV2.claudePromptUuid(attemptId), + }); yield* harness.runtime.startTurn( makeClaudeTestTurnInput({ @@ -5596,7 +5613,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { : [], ); assert.deepEqual([...new Set(userRunItems)], ["assistant_message"]); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5702,7 +5719,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { finalSubagent?.type === "subagent.updated" && finalSubagent.subagent.status, "completed", ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5779,7 +5796,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { "Part two.", ]); assert.deepEqual(routing.assistantTexts(harness.threadId), ["The auditor finished."]); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5840,7 +5857,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { : [], ); assert.deepEqual(retryStatuses, ["running"]); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5929,7 +5946,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { FINAL_REPORT, ]); assert.deepEqual(routing.assistantTexts(harness.threadId), ["The auditor finished."]); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -6036,7 +6053,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ); const child = harness.events.find((event) => event.type === "app_thread.created"); assert.equal(child?.appThread.modelSelection?.model, initialModel ?? parentModel); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -6117,7 +6134,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { }), ); yield* awaitUntil(() => harness.terminalEvents().length === 1, "turn terminal"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -6206,7 +6223,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { assert.equal(subagentEvents().at(-1)?.subagent.status, "cancelled"); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -6456,7 +6473,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { // The completion keeps the resuming run's attribution. assert.equal(finalSubagent?.runId, "run-attempt-claude-wake-9c"); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -6464,15 +6481,15 @@ describe("ClaudeAdapterV2 background wake turns", () => { Effect.scoped( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-v2-process-reset-", }); const processQueues: Array> = []; const events: Array = []; const continuationRequests: Array = []; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: {}, attachmentsDir, @@ -6623,7 +6640,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { () => events.filter((event) => event.type === "turn.terminal").length === 2, "second turn terminal", ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -6633,15 +6650,15 @@ describe("ClaudeAdapterV2 background wake turns", () => { Effect.scoped( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-v2-buffer-replace-", }); const processQueues: Array> = []; const events: Array = []; const continuationRequests: Array = []; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: {}, attachmentsDir, @@ -6808,7 +6825,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ), ); assert.isFalse(yield* hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -6867,15 +6884,15 @@ describe("ClaudeAdapterV2 background wake turns", () => { }); const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-v2-subagent-buffer-replace-", }); const processQueues: Array> = []; const events: Array = []; const continuationRequests: Array = []; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: {}, attachmentsDir, @@ -7049,7 +7066,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ); assert.equal(subagentNodeEvents.at(-1)?.node.status, "completed"); assert.isFalse(yield* hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -7059,15 +7076,15 @@ describe("ClaudeAdapterV2 background wake turns", () => { Effect.scoped( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-v2-replace-open-fail-", }); let openCount = 0; const processQueues: Array> = []; const events: Array = []; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: {}, attachmentsDir, @@ -7083,7 +7100,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { openCount += 1; if (openCount === 2) { return Effect.fail( - new ClaudeAgentSdkQueryRunnerError({ + new ClaudeAdapterV2.ClaudeAgentSdkQueryRunnerError({ method: "open", cause: "forced replacement open failure", }), @@ -7183,7 +7200,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { "roster cleared after failed same-thread replacement open", ); assert.isFalse(yield* hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -7193,7 +7210,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { Effect.scoped( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-v2-replace-open-fail-wake-", }); @@ -7201,8 +7218,8 @@ describe("ClaudeAdapterV2 background wake turns", () => { const processQueues: Array> = []; const events: Array = []; const continuationRequests: Array = []; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: {}, attachmentsDir, @@ -7221,7 +7238,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { openCount += 1; if (openCount === 2) { return Effect.fail( - new ClaudeAgentSdkQueryRunnerError({ + new ClaudeAdapterV2.ClaudeAgentSdkQueryRunnerError({ method: "open", cause: "forced replacement open failure", }), @@ -7381,7 +7398,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { "continuation request after retry", ); assert.equal(continuationRequests[1]?.detail, "Retry build completed successfully"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -7389,13 +7406,13 @@ describe("ClaudeAdapterV2 background wake turns", () => { Effect.scoped( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-claude-v2-first-open-fail-", }); const events: Array = []; - const adapter = makeClaudeAdapterV2({ - instanceId: CLAUDE_DEFAULT_INSTANCE_ID, + const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ + instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, settings: DEFAULT_CLAUDE_SETTINGS, environment: {}, attachmentsDir, @@ -7409,7 +7426,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { allocateSessionId: Effect.succeed(WAKE_NATIVE_SESSION), open: () => Effect.fail( - new ClaudeAgentSdkQueryRunnerError({ + new ClaudeAdapterV2.ClaudeAgentSdkQueryRunnerError({ method: "open", cause: "forced first open failure", }), @@ -7455,7 +7472,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { assert.isTrue(Exit.isFailure(failedStart)); // No live process ever existed: do not emit a fabricated empty roster. assert.lengthOf(providerThreadRosterEvents(events), 0); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); }); @@ -7495,7 +7512,10 @@ describe("ClaudeAdapterV2 query message stream", () => { } as unknown as ClaudeQuery; const scope = yield* Scope.make(); - yield* Stream.fromAsyncIterable(claudeQueryMessages(query), (cause) => cause).pipe( + yield* Stream.fromAsyncIterable( + ClaudeAdapterV2.claudeQueryMessages(query), + (cause) => cause, + ).pipe( Stream.runForEach(() => Effect.void), Effect.forkIn(scope), ); diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.testkit.ts index ba87135a84d0..a709dfddf02d 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.testkit.ts @@ -24,30 +24,15 @@ import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import { isWindowsClaudeLauncherShimPath, resolveClaudeSdkExecutablePath, } from "../../provider/Drivers/ClaudeExecutable.ts"; -import { - CLAUDE_PROVIDER, - CLAUDE_DEFAULT_INSTANCE_ID, - ClaudeAdapterV2Driver, - ClaudeAgentSdkQueryRunner, - ClaudeAgentSdkQueryRunnerError, - claudePromptUuid, - makeClaudeUserMessage, - makeClaudeQueryOptions, - type ClaudeAgentSdkSessionForkInput, - type ClaudeAgentSdkSubagentLookupInput, - type ClaudeAgentSdkQueryOpenInput, - type ClaudeAgentSdkQueryOptions, - type ClaudeAgentSdkQuerySession, - type ClaudeAgentSdkQueryTools, -} from "./ClaudeAdapterV2.ts"; -import { layer as idAllocatorLayer } from "../IdAllocator.ts"; +import * as ClaudeAdapterV2 from "./ClaudeAdapterV2.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { ProviderAdapterDriverCreateError } from "../ProviderAdapterDriver.ts"; -import { makeDriverLayer as makeProviderAdapterRegistryDriverLayer } from "../ProviderAdapterRegistry.ts"; +import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; import { randomUuidV4 } from "../RandomUuid.ts"; import { makeReplayServerConfig, @@ -64,7 +49,7 @@ export const claudeBackgroundWakeResultLabel = (wakeNumber: number) => `result:background-wake:${wakeNumber}`; const ClaudeAgentSdkReplayTranscript = Schema.Struct({ - provider: Schema.Literal(CLAUDE_PROVIDER), + provider: Schema.Literal(ClaudeAdapterV2.CLAUDE_PROVIDER), protocol: Schema.Literal(CLAUDE_AGENT_SDK_REPLAY_PROTOCOL), version: Schema.String, scenario: Schema.String, @@ -186,7 +171,7 @@ export type ClaudeOrchestratorReplayHarnessError = typeof ClaudeOrchestratorRepl interface ClaudeQueryOpenFrame { readonly type: "query.open"; - readonly options: ClaudeAgentSdkQueryOptions; + readonly options: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions; } interface ClaudePromptOfferFrame { @@ -260,9 +245,15 @@ type ClaudeOutboundFrame = | ClaudeSubagentLookupFrame; interface ClaudeQueryRunner { - readonly open: (input: ClaudeAgentSdkQueryOpenInput) => ClaudeAgentSdkQuerySession; - readonly forkSession: (input: ClaudeAgentSdkSessionForkInput) => ClaudeSessionForkedFrame; - readonly subagentLaunchToolUseId: (input: ClaudeAgentSdkSubagentLookupInput) => string | null; + readonly open: ( + input: ClaudeAdapterV2.ClaudeAgentSdkQueryOpenInput, + ) => ClaudeAdapterV2.ClaudeAgentSdkQuerySession; + readonly forkSession: ( + input: ClaudeAdapterV2.ClaudeAgentSdkSessionForkInput, + ) => ClaudeSessionForkedFrame; + readonly subagentLaunchToolUseId: ( + input: ClaudeAdapterV2.ClaudeAgentSdkSubagentLookupInput, + ) => string | null; readonly assertComplete: () => void; } @@ -425,7 +416,9 @@ async function waitForReplayDelay(afterMs: number, signal: AbortSignal): Promise } } -function stableClaudeQueryOptions(options: ClaudeAgentSdkQueryOptions): ClaudeAgentSdkQueryOptions { +function stableClaudeQueryOptions( + options: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions, +): ClaudeAdapterV2.ClaudeAgentSdkQueryOptions { const stable = { model: options.model, tools: options.tools, @@ -445,7 +438,7 @@ function stableClaudeQueryOptions(options: ClaudeAgentSdkQueryOptions): ClaudeAg } function makeClaudeQueryOpenFrame( - input: Pick, + input: Pick, ): ClaudeQueryOpenFrame { return { type: "query.open", @@ -461,7 +454,7 @@ function makeClaudePromptOfferFrame(message: SDKUserMessage): ClaudePromptOfferF } function makeClaudeSessionForkFrame( - input: ClaudeAgentSdkSessionForkInput, + input: ClaudeAdapterV2.ClaudeAgentSdkSessionForkInput, scenario: string, ): ClaudeSessionForkFrame { return { @@ -514,7 +507,7 @@ function makeReplayQueryRunner( }; async function* replayMessages( - options: ClaudeAgentSdkQueryOptions, + options: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions, signal: AbortSignal, ): AsyncGenerator { while (true) { @@ -615,7 +608,7 @@ function makeReplayQueryRunner( } const replayMessagesWithGateCleanup = ( - options: ClaudeAgentSdkQueryOptions, + options: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions, ): AsyncIterable => ({ [Symbol.asyncIterator]: () => { const abortController = new AbortController(); @@ -874,20 +867,20 @@ function nativeSessionIdFor(transcript: ClaudeAgentSdkReplayTranscript): string : "00000000-0000-4000-8000-000000000000"; } -const isClaudeAgentSdkQueryRunnerError = Schema.is(ClaudeAgentSdkQueryRunnerError); +const isClaudeAgentSdkQueryRunnerError = Schema.is(ClaudeAdapterV2.ClaudeAgentSdkQueryRunnerError); const isClaudeAgentSdkReplayError = Schema.is(ClaudeAgentSdkReplayError); function replayQueryRunnerError( transcript: ClaudeAgentSdkReplayTranscript, cause: unknown, -): ClaudeAgentSdkQueryRunnerError { +): ClaudeAdapterV2.ClaudeAgentSdkQueryRunnerError { if (isClaudeAgentSdkQueryRunnerError(cause)) { return cause; } const replayCause = isClaudeAgentSdkReplayError(cause) ? cause : new ClaudeReplayDriverError({ scenario: transcript.scenario, cause }); - return new ClaudeAgentSdkQueryRunnerError({ + return new ClaudeAdapterV2.ClaudeAgentSdkQueryRunnerError({ cause: replayCause, method: `replay-scenario:${transcript.scenario}`, }); @@ -912,13 +905,13 @@ const makeClaudeAgentSdkReplayQueryRunner = Effect.fn("ClaudeAgentSdkReplayQuery function replayQueryRunnerService( transcript: ClaudeAgentSdkReplayTranscript, queryRunner: ClaudeQueryRunner, -): ClaudeAgentSdkQueryRunner["Service"] { +): ClaudeAdapterV2.ClaudeAgentSdkQueryRunner["Service"] { const replay = (run: () => A) => Effect.try({ try: run, catch: (cause) => replayQueryRunnerError(transcript, cause), }); - return ClaudeAgentSdkQueryRunner.of({ + return ClaudeAdapterV2.ClaudeAgentSdkQueryRunner.of({ allocateSessionId: Effect.succeed(nativeSessionIdFor(transcript)), open: (input) => replay(() => queryRunner.open(input)), forkSession: (input) => replay(() => queryRunner.forkSession(input)), @@ -930,9 +923,9 @@ function replayQueryRunnerService( function makeClaudeAgentSdkReplayQueryRunnerLayer( transcript: ClaudeAgentSdkReplayTranscript, options: { readonly replayGate?: ProviderReplayGate } = {}, -): Layer.Layer { +): Layer.Layer { return Layer.effect( - ClaudeAgentSdkQueryRunner, + ClaudeAdapterV2.ClaudeAgentSdkQueryRunner, makeClaudeAgentSdkReplayQueryRunner(transcript, options), ); } @@ -944,16 +937,16 @@ function makeClaudeAgentSdkReplayLayer( // Shared across runtimes; its owner asserts completion. readonly queryRunner?: ClaudeQueryRunner; } = {}, -): Layer.Layer { +): Layer.Layer { if (options.queryRunner !== undefined) { return Layer.succeed( - ClaudeAgentSdkQueryRunner, + ClaudeAdapterV2.ClaudeAgentSdkQueryRunner, replayQueryRunnerService(transcript, options.queryRunner), ); } const queryRunner = makeReplayQueryRunner(transcript, options); return Layer.effect( - ClaudeAgentSdkQueryRunner, + ClaudeAdapterV2.ClaudeAgentSdkQueryRunner, Effect.gen(function* () { yield* Effect.addFinalizer(() => Effect.sync(() => { @@ -973,21 +966,21 @@ function makeClaudeProviderAdapterRegistryReplayLayer( } = {}, ) { const serverConfigLayer = Layer.effect( - ServerConfig, + ServerConfig.ServerConfig, makeReplayServerConfig(transcript.scenario).pipe(Effect.orDie), ).pipe(Layer.provide(NodeServices.layer)); - return makeProviderAdapterRegistryDriverLayer({ - drivers: [ClaudeAdapterV2Driver], + return ProviderAdapterRegistry.makeDriverLayer({ + drivers: [ClaudeAdapterV2.ClaudeAdapterV2Driver], configMap: { - [CLAUDE_DEFAULT_INSTANCE_ID]: { - driver: CLAUDE_PROVIDER, + [ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID]: { + driver: ClaudeAdapterV2.CLAUDE_PROVIDER, }, }, }).pipe( Layer.provide( Layer.mergeAll( makeClaudeAgentSdkReplayLayer(transcript, options), - idAllocatorLayer, + IdAllocator.layer, NodeServices.layer, serverConfigLayer, ), @@ -1457,8 +1450,8 @@ async function recordClaudeStreamingQuery(input: { readonly sessionId: string; readonly entries: Array; readonly enableTools?: boolean; - readonly tools?: ClaudeAgentSdkQueryTools; - readonly permissionMode?: ClaudeAgentSdkQueryOptions["permissionMode"]; + readonly tools?: ClaudeAdapterV2.ClaudeAgentSdkQueryTools; + readonly permissionMode?: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions["permissionMode"]; readonly allowedTools?: ReadonlyArray; readonly disallowedTools?: ReadonlyArray; readonly allowDangerouslySkipPermissions?: boolean; @@ -1503,7 +1496,7 @@ async function recordClaudeStreamingQuery(input: { return result; } : undefined; - const options = makeClaudeQueryOptions({ + const options = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: input.modelSelection, nativeThreadId: input.sessionId, resume: false, @@ -1564,9 +1557,9 @@ async function recordClaudeStreamingQuery(input: { try { for (const [index, prompt] of input.prompts.entries()) { // Like the adapter, give each prompt a uuid Claude echoes on its turn. - const message = makeClaudeUserMessage({ + const message = ClaudeAdapterV2.makeClaudeUserMessage({ text: prompt, - uuid: claudePromptUuid(`${input.sessionId}:prompt:${index + 1}`), + uuid: ClaudeAdapterV2.claudePromptUuid(`${input.sessionId}:prompt:${index + 1}`), }); input.entries.push({ type: "expect_outbound", @@ -1621,8 +1614,8 @@ async function recordClaudeActiveSteeringQuery(input: { readonly sessionId: string; readonly entries: Array; readonly enableTools?: boolean; - readonly tools?: ClaudeAgentSdkQueryTools; - readonly permissionMode?: ClaudeAgentSdkQueryOptions["permissionMode"]; + readonly tools?: ClaudeAdapterV2.ClaudeAgentSdkQueryTools; + readonly permissionMode?: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions["permissionMode"]; readonly allowedTools?: ReadonlyArray; readonly disallowedTools?: ReadonlyArray; readonly allowDangerouslySkipPermissions?: boolean; @@ -1636,7 +1629,7 @@ async function recordClaudeActiveSteeringQuery(input: { const promptQueue = new RecordingPromptQueue(); const offeredPrompts = new Set(); const offerPrompt = (index: number, priority?: SDKUserMessage["priority"]) => { - const message = makeClaudeUserMessage({ + const message = ClaudeAdapterV2.makeClaudeUserMessage({ text: input.prompts[index]!, ...(priority === undefined ? {} : { priority }), }); @@ -1685,7 +1678,7 @@ async function recordClaudeActiveSteeringQuery(input: { return result; } : undefined; - const options = makeClaudeQueryOptions({ + const options = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: input.modelSelection, nativeThreadId: input.sessionId, resume: false, @@ -1756,15 +1749,15 @@ async function recordClaudeRestartingQueries(input: { readonly sessionId: string; readonly entries: Array; readonly enableTools?: boolean; - readonly tools?: ClaudeAgentSdkQueryTools; - readonly permissionMode?: ClaudeAgentSdkQueryOptions["permissionMode"]; + readonly tools?: ClaudeAdapterV2.ClaudeAgentSdkQueryTools; + readonly permissionMode?: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions["permissionMode"]; readonly allowedTools?: ReadonlyArray; readonly disallowedTools?: ReadonlyArray; readonly allowDangerouslySkipPermissions?: boolean; }): Promise { for (const [index, prompt] of input.prompts.entries()) { const promptQueue = new RecordingPromptQueue(); - const options = makeClaudeQueryOptions({ + const options = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: input.modelSelection, nativeThreadId: input.sessionId, resume: index > 0, @@ -1789,7 +1782,7 @@ async function recordClaudeRestartingQueries(input: { label: `query.open:${index + 1}`, frame: makeClaudeQueryOpenFrame({ options }), }); - const message = makeClaudeUserMessage({ text: prompt }); + const message = ClaudeAdapterV2.makeClaudeUserMessage({ text: prompt }); input.entries.push({ type: "expect_outbound", label: `prompt.offer:${index + 1}`, @@ -1844,8 +1837,8 @@ async function recordClaudeResumeAtCursorQuery(input: { readonly entries: Array; readonly metadata: Record; readonly enableTools?: boolean; - readonly tools?: ClaudeAgentSdkQueryTools; - readonly permissionMode?: ClaudeAgentSdkQueryOptions["permissionMode"]; + readonly tools?: ClaudeAdapterV2.ClaudeAgentSdkQueryTools; + readonly permissionMode?: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions["permissionMode"]; readonly allowedTools?: ReadonlyArray; readonly disallowedTools?: ReadonlyArray; readonly allowDangerouslySkipPermissions?: boolean; @@ -1857,7 +1850,7 @@ async function recordClaudeResumeAtCursorQuery(input: { } const sourcePromptQueue = new RecordingPromptQueue(); - const sourceOptions = makeClaudeQueryOptions({ + const sourceOptions = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: input.modelSelection, nativeThreadId: input.sessionId, resume: false, @@ -1891,7 +1884,7 @@ async function recordClaudeResumeAtCursorQuery(input: { try { const sourceCursors: Array = []; for (const [index, prompt] of input.prompts.slice(0, 2).entries()) { - const message = makeClaudeUserMessage({ text: prompt }); + const message = ClaudeAdapterV2.makeClaudeUserMessage({ text: prompt }); input.entries.push({ type: "expect_outbound", label: `prompt.offer:${index + 1}`, @@ -1927,7 +1920,7 @@ async function recordClaudeResumeAtCursorQuery(input: { const resumedPromptQueue = new RecordingPromptQueue(); const resumedOptions = { - ...makeClaudeQueryOptions({ + ...ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: input.modelSelection, nativeThreadId: input.sessionId, resume: true, @@ -1947,13 +1940,13 @@ async function recordClaudeResumeAtCursorQuery(input: { : {}), }), resumeSessionAt, - } satisfies ClaudeAgentSdkQueryOptions; + } satisfies ClaudeAdapterV2.ClaudeAgentSdkQueryOptions; input.entries.push({ type: "expect_outbound", label: "query.open:resume_at_cursor", frame: makeClaudeQueryOpenFrame({ options: resumedOptions }), }); - const resumedMessage = makeClaudeUserMessage({ text: input.prompts[2]! }); + const resumedMessage = ClaudeAdapterV2.makeClaudeUserMessage({ text: input.prompts[2]! }); input.entries.push({ type: "expect_outbound", label: "prompt.offer:3", @@ -2008,8 +2001,8 @@ async function recordClaudeForkSessionQuery(input: { readonly forkPromptGroups?: ReadonlyArray>; readonly sourceContinuationPromptCount?: number; readonly enableTools?: boolean; - readonly tools?: ClaudeAgentSdkQueryTools; - readonly permissionMode?: ClaudeAgentSdkQueryOptions["permissionMode"]; + readonly tools?: ClaudeAdapterV2.ClaudeAgentSdkQueryTools; + readonly permissionMode?: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions["permissionMode"]; readonly allowedTools?: ReadonlyArray; readonly disallowedTools?: ReadonlyArray; readonly allowDangerouslySkipPermissions?: boolean; @@ -2040,7 +2033,7 @@ async function recordClaudeForkSessionQuery(input: { } const sourcePromptQueue = new RecordingPromptQueue(); - const sourceOptions = makeClaudeQueryOptions({ + const sourceOptions = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: input.modelSelection, nativeThreadId: input.sessionId, resume: false, @@ -2073,7 +2066,7 @@ async function recordClaudeForkSessionQuery(input: { try { const sourceCursors: Array = []; for (const [index, prompt] of input.prompts.slice(0, sourcePromptCount).entries()) { - const message = makeClaudeUserMessage({ text: prompt }); + const message = ClaudeAdapterV2.makeClaudeUserMessage({ text: prompt }); input.entries.push({ type: "expect_outbound", label: `prompt.offer:${index + 1}`, @@ -2148,7 +2141,7 @@ async function recordClaudeForkSessionQuery(input: { }); const targetPromptQueue = new RecordingPromptQueue(); - const targetOptions = makeClaudeQueryOptions({ + const targetOptions = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: input.modelSelection, nativeThreadId: forked.sessionId, resume: true, @@ -2179,7 +2172,7 @@ async function recordClaudeForkSessionQuery(input: { const targetIterator = targetRuntime[Symbol.asyncIterator](); for (const prompt of forkPrompts) { promptOrdinal += 1; - const targetMessage = makeClaudeUserMessage({ text: prompt }); + const targetMessage = ClaudeAdapterV2.makeClaudeUserMessage({ text: prompt }); input.entries.push({ type: "expect_outbound", label: `prompt.offer:${promptOrdinal}`, @@ -2206,7 +2199,7 @@ async function recordClaudeForkSessionQuery(input: { input.metadata.forkedNativeSessionIds = forkedNativeSessionIds; if (sourceContinuationPromptCount > 0) { const continuationPromptQueue = new RecordingPromptQueue(); - const continuationOptions = makeClaudeQueryOptions({ + const continuationOptions = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: input.modelSelection, nativeThreadId: input.sessionId, resume: true, @@ -2237,7 +2230,7 @@ async function recordClaudeForkSessionQuery(input: { const continuationIterator = continuationRuntime[Symbol.asyncIterator](); for (const prompt of input.prompts.slice(forkPromptEnd)) { promptOrdinal += 1; - const continuationMessage = makeClaudeUserMessage({ text: prompt }); + const continuationMessage = ClaudeAdapterV2.makeClaudeUserMessage({ text: prompt }); input.entries.push({ type: "expect_outbound", label: `prompt.offer:${promptOrdinal}`, @@ -2290,14 +2283,14 @@ async function recordInterruptedClaudeQuery(input: { // With interruptAfter "tool_use": interrupt after this many root tool uses. readonly interruptAfterToolUses?: number; readonly enableTools?: boolean; - readonly tools?: ClaudeAgentSdkQueryTools; - readonly permissionMode?: ClaudeAgentSdkQueryOptions["permissionMode"]; + readonly tools?: ClaudeAdapterV2.ClaudeAgentSdkQueryTools; + readonly permissionMode?: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions["permissionMode"]; readonly allowedTools?: ReadonlyArray; readonly disallowedTools?: ReadonlyArray; readonly allowDangerouslySkipPermissions?: boolean; }): Promise { const promptQueue = new RecordingPromptQueue(); - const options = makeClaudeQueryOptions({ + const options = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: input.modelSelection, nativeThreadId: input.sessionId, resume: input.resume, @@ -2326,7 +2319,7 @@ async function recordInterruptedClaudeQuery(input: { options, }); const iterator = runtime[Symbol.asyncIterator](); - const message = makeClaudeUserMessage({ text: input.prompt }); + const message = ClaudeAdapterV2.makeClaudeUserMessage({ text: input.prompt }); input.entries.push({ type: "expect_outbound", label: input.promptOfferLabel, @@ -2394,8 +2387,8 @@ async function recordClaudeInterruptQuery(input: { readonly sessionId: string; readonly entries: Array; readonly enableTools?: boolean; - readonly tools?: ClaudeAgentSdkQueryTools; - readonly permissionMode?: ClaudeAgentSdkQueryOptions["permissionMode"]; + readonly tools?: ClaudeAdapterV2.ClaudeAgentSdkQueryTools; + readonly permissionMode?: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions["permissionMode"]; readonly allowedTools?: ReadonlyArray; readonly disallowedTools?: ReadonlyArray; readonly allowDangerouslySkipPermissions?: boolean; @@ -2442,8 +2435,8 @@ async function recordClaudeInterruptRestartQuery(input: { readonly sessionId: string; readonly entries: Array; readonly enableTools?: boolean; - readonly tools?: ClaudeAgentSdkQueryTools; - readonly permissionMode?: ClaudeAgentSdkQueryOptions["permissionMode"]; + readonly tools?: ClaudeAdapterV2.ClaudeAgentSdkQueryTools; + readonly permissionMode?: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions["permissionMode"]; readonly allowedTools?: ReadonlyArray; readonly disallowedTools?: ReadonlyArray; readonly allowDangerouslySkipPermissions?: boolean; @@ -2478,7 +2471,7 @@ async function recordClaudeInterruptRestartQuery(input: { }); const secondPromptQueue = new RecordingPromptQueue(); - const secondOptions = makeClaudeQueryOptions({ + const secondOptions = ClaudeAdapterV2.makeClaudeQueryOptions({ modelSelection: input.modelSelection, nativeThreadId: input.sessionId, resume: true, @@ -2502,7 +2495,7 @@ async function recordClaudeInterruptRestartQuery(input: { label: "query.open:2", frame: makeClaudeQueryOpenFrame({ options: secondOptions }), }); - const secondMessage = makeClaudeUserMessage({ text: input.prompts[1]! }); + const secondMessage = ClaudeAdapterV2.makeClaudeUserMessage({ text: input.prompts[1]! }); input.entries.push({ type: "expect_outbound", label: "prompt.offer:2", @@ -2558,8 +2551,8 @@ export async function recordClaudeAgentSdkReplayTranscript(input: { | "interrupt" | "interrupt_restart"; readonly enableTools?: boolean; - readonly tools?: ClaudeAgentSdkQueryTools; - readonly permissionMode?: ClaudeAgentSdkQueryOptions["permissionMode"]; + readonly tools?: ClaudeAdapterV2.ClaudeAgentSdkQueryTools; + readonly permissionMode?: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions["permissionMode"]; readonly allowedTools?: ReadonlyArray; readonly disallowedTools?: ReadonlyArray; readonly allowDangerouslySkipPermissions?: boolean; @@ -2756,7 +2749,7 @@ export async function recordClaudeAgentSdkReplayTranscript(input: { } return { - provider: CLAUDE_PROVIDER, + provider: ClaudeAdapterV2.CLAUDE_PROVIDER, protocol: CLAUDE_AGENT_SDK_REPLAY_PROTOCOL, version: "0.2.111", scenario: input.scenario, @@ -2798,7 +2791,7 @@ export const ClaudeOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarnes ClaudeAgentSdkReplayTranscript, ClaudeOrchestratorReplayHarnessError > = { - driver: CLAUDE_PROVIDER, + driver: ClaudeAdapterV2.CLAUDE_PROVIDER, decodeTranscript: (transcript) => decodeClaudeAgentSdkReplayTranscript(transcript).pipe( Effect.mapError( diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index 68dda79bd821..f6346b05d245 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -85,7 +85,7 @@ import { resolveAttachmentPath } from "../../attachmentStore.ts"; import { planClaudeSkillDispatch } from "../../provider/Drivers/ClaudeSkillDispatch.ts"; import { discoverClaudeSkills } from "../../provider/Drivers/ClaudeSkills.ts"; import { compileClaudeModelSelection } from "../../claudeModelOptions.ts"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import { expandHomePath } from "../../pathExpansion.ts"; import { claudeSignedOutMessage, @@ -101,7 +101,7 @@ import { type EventNdjsonLogger, shouldPersistProviderEvent, } from "../../provider/Layers/EventNdjsonLogger.ts"; -import { ProviderEventLoggers } from "../../provider/Layers/ProviderEventLoggers.ts"; +import * as ProviderEventLoggers from "../../provider/Layers/ProviderEventLoggers.ts"; import { claudeRateLimitEventToUpdate, type ClaudeScopedLimitNames, @@ -111,45 +111,18 @@ import { mergeProviderInstanceEnvironment } from "../../provider/ProviderInstanc import { T3_CODE_ORCHESTRATION_INSTRUCTIONS } from "../../provider/T3OrchestrationInstructions.ts"; import { buildRuntimeInstructions } from "../../provider/RuntimeInstructions.ts"; import * as McpProviderSession from "../../mcp/McpProviderSession.ts"; -import { IdAllocatorV2, type IdAllocatorV2Shape } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { makeProviderFailure, makeProviderRetryTurnItem } from "../ProviderFailure.ts"; import { turnScopedSelectionTransition } from "../ProviderSelectionTransition.ts"; import { providerMessageTextWithAttachmentPaths } from "../AttachmentPrompt.ts"; -import { - ProviderAdapterEnsureThreadError, - ProviderAdapterForkThreadError, - ProviderAdapterInterruptError, - ProviderAdapterOpenSessionError, - ProviderAdapterProtocolError, - ProviderAdapterReadThreadSnapshotError, - ProviderAdapterResumeThreadError, - ProviderAdapterRollbackThreadError, - ProviderAdapterRuntimeRequestResponseError, - ProviderAdapterSteerRunError, - ProviderAdapterTurnStartError, - ProviderAdapterV2, - type ProviderAdapterV2EnsureThreadInput, - type ProviderAdapterV2Event, - type ProviderAdapterV2ForkThreadInput, - type ProviderAdapterV2InterruptInput, - type ProviderAdapterV2OpenSessionInput, - type ProviderAdapterV2RollbackThreadInput, - type ProviderAdapterV2RuntimePolicy, - type ProviderAdapterV2Shape, - type ProviderAdapterV2SessionRuntime, - type ProviderAdapterV2SteerInput, - type ProviderAdapterV2TurnInput, -} from "../ProviderAdapter.ts"; +import * as ProviderAdapter from "../ProviderAdapter.ts"; import { ProviderAdapterDriverCreateError, type ProviderAdapterDriver, type ProviderAdapterDriverCreateInput, } from "../ProviderAdapterDriver.ts"; import { type BackgroundWorkReport, backgroundWorkNotification } from "../Notification.ts"; -import { - type ProviderContinuationRequest, - ProviderContinuationRequests, -} from "../ProviderContinuationRequests.ts"; +import * as ProviderContinuationRequests from "../ProviderContinuationRequests.ts"; import { makeSubagentChildThread, makeSubagentConversationArtifacts, @@ -403,9 +376,11 @@ export interface ClaudeAgentSdkSubagentLookupInput { const isClaudeAgentSdkQueryRunnerError = Schema.is(ClaudeAgentSdkQueryRunnerError); const isProviderAdapterRuntimeRequestResponseError = Schema.is( - ProviderAdapterRuntimeRequestResponseError, + ProviderAdapter.ProviderAdapterRuntimeRequestResponseError, +); +const isProviderAdapterRollbackThreadError = Schema.is( + ProviderAdapter.ProviderAdapterRollbackThreadError, ); -const isProviderAdapterRollbackThreadError = Schema.is(ProviderAdapterRollbackThreadError); function queryRunnerError(cause: unknown, method: string): ClaudeAgentSdkQueryRunnerError { return isClaudeAgentSdkQueryRunnerError(cause) @@ -593,12 +568,12 @@ export function makeClaudeAgentSdkProtocolLogger(input: { export const claudeAgentSdkQueryRunnerLiveLayer: Layer.Layer< ClaudeAgentSdkQueryRunner, never, - Crypto.Crypto | ProviderEventLoggers + Crypto.Crypto | ProviderEventLoggers.ProviderEventLoggers > = Layer.effect( ClaudeAgentSdkQueryRunner, Effect.gen(function* () { const crypto = yield* Crypto.Crypto; - const { native: nativeEventLogger } = yield* ProviderEventLoggers; + const { native: nativeEventLogger } = yield* ProviderEventLoggers.ProviderEventLoggers; return ClaudeAgentSdkQueryRunner.of({ allocateSessionId: crypto.randomUUIDv4.pipe( @@ -1019,7 +994,7 @@ function resultTextFromSdkMessage( } function makeProviderThread(input: { - readonly idAllocator: IdAllocatorV2Shape; + readonly idAllocator: IdAllocator.IdAllocatorV2Shape; readonly providerInstanceId: ProviderInstanceId; readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"]; readonly ownerNodeId?: OrchestrationV2ProviderThread["ownerNodeId"]; @@ -1059,7 +1034,7 @@ const getNativeThreadId = Effect.fnUntraced(function* ( ) { const nativeThreadId = providerThread.nativeThreadRef?.nativeId; if (nativeThreadId === undefined || nativeThreadId === null) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Provider thread ${providerThread.id} is missing a native Claude session id.`, }); @@ -1083,13 +1058,13 @@ const getNativeConversationHeadId = Effect.fnUntraced(function* ( return undefined; } if (nativeHeadRef.driver !== CLAUDE_PROVIDER) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Provider thread ${providerThread.id} has a non-Claude native conversation head reference.`, }); } if (nativeHeadRef.nativeId === null) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Provider thread ${providerThread.id} has a Claude native conversation head reference without a native id.`, }); @@ -1098,7 +1073,7 @@ const getNativeConversationHeadId = Effect.fnUntraced(function* ( }); const resolveClaudeForkUpToMessageId = Effect.fn("ClaudeAdapterV2.resolveForkUpToMessageId")( - function* (input: ProviderAdapterV2ForkThreadInput) { + function* (input: ProviderAdapter.ProviderAdapterV2ForkThreadInput) { if (input.providerTurnId === undefined || input.sourceProviderTurns === undefined) { return undefined; } @@ -1108,7 +1083,7 @@ const resolveClaudeForkUpToMessageId = Effect.fn("ClaudeAdapterV2.resolveForkUpT .toSorted((left, right) => left.ordinal - right.ordinal); const boundaryIndex = sourceTurns.findIndex((turn) => turn.id === input.providerTurnId); if (boundaryIndex < 0) { - return yield* new ProviderAdapterForkThreadError({ + return yield* new ProviderAdapter.ProviderAdapterForkThreadError({ driver: CLAUDE_PROVIDER, providerThreadId: input.sourceProviderThread.id, cause: `Cannot fork Claude thread from provider turn ${input.providerTurnId}: source turn was not found in provider thread ${input.sourceProviderThread.id}.`, @@ -1131,7 +1106,7 @@ const resolveClaudeForkUpToMessageId = Effect.fn("ClaudeAdapterV2.resolveForkUpT return undefined; } - return yield* new ProviderAdapterForkThreadError({ + return yield* new ProviderAdapter.ProviderAdapterForkThreadError({ driver: CLAUDE_PROVIDER, providerThreadId: input.sourceProviderThread.id, cause: `Cannot fork Claude thread from prior provider turn ${input.providerTurnId}: no SDK assistant message cursor was recorded for that turn.`, @@ -1141,14 +1116,14 @@ const resolveClaudeForkUpToMessageId = Effect.fn("ClaudeAdapterV2.resolveForkUpT const resolveClaudeRollbackResumeSessionAt = Effect.fn( "ClaudeAdapterV2.resolveRollbackResumeSessionAt", -)(function* (input: ProviderAdapterV2RollbackThreadInput) { +)(function* (input: ProviderAdapter.ProviderAdapterV2RollbackThreadInput) { switch (input.target.type) { case "thread_start": return null; case "provider_turn": { const target = input.target; if (target.providerTurn.providerThreadId !== input.providerThread.id) { - return yield* new ProviderAdapterRollbackThreadError({ + return yield* new ProviderAdapter.ProviderAdapterRollbackThreadError({ driver: CLAUDE_PROVIDER, providerThreadId: input.providerThread.id, cause: `Cannot roll back Claude thread ${input.providerThread.id} to provider turn ${target.providerTurn.id}: target turn belongs to provider thread ${target.providerTurn.providerThreadId}.`, @@ -1172,7 +1147,7 @@ const resolveClaudeRollbackResumeSessionAt = Effect.fn( return null; } - return yield* new ProviderAdapterRollbackThreadError({ + return yield* new ProviderAdapter.ProviderAdapterRollbackThreadError({ driver: CLAUDE_PROVIDER, providerThreadId: input.providerThread.id, cause: `Cannot roll back Claude thread ${input.providerThread.id} to provider turn ${target.providerTurn.id}: no SDK assistant message cursor was recorded for that turn.`, @@ -1274,7 +1249,7 @@ const makeClaudeUserMessageWithAttachments = Effect.fnUntraced(function* (input: continue; } if (!isSupportedClaudeImageMimeType(attachment.mimeType)) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Unsupported Claude image attachment type '${attachment.mimeType}'`, }); @@ -1285,7 +1260,7 @@ const makeClaudeUserMessageWithAttachments = Effect.fnUntraced(function* (input: attachment, }); if (attachmentPath === null) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Invalid attachment id '${attachment.id}'`, }); @@ -1294,7 +1269,7 @@ const makeClaudeUserMessageWithAttachments = Effect.fnUntraced(function* (input: const bytes = yield* input.fileSystem.readFile(attachmentPath).pipe( Effect.mapError( (cause) => - new ProviderAdapterProtocolError({ + new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Failed to read attachment '${attachment.id}'`, payload: cause, @@ -1395,7 +1370,7 @@ const isClaudeRuntimeReadOnlyFullAccessSandboxPolicy = Schema.is( ); function sandboxPolicyKindForClaudeRuntimePolicy( - runtimePolicy: ProviderAdapterV2RuntimePolicy, + runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy, ): ClaudeRuntimeSandboxPolicyKindName | undefined { return runtimePolicy.sandboxPolicy !== undefined && isClaudeRuntimeSandboxPolicyKind(runtimePolicy.sandboxPolicy) @@ -1403,7 +1378,9 @@ function sandboxPolicyKindForClaudeRuntimePolicy( : undefined; } -function readOnlyPolicyAllowsGlobalReads(runtimePolicy: ProviderAdapterV2RuntimePolicy): boolean { +function readOnlyPolicyAllowsGlobalReads( + runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy, +): boolean { return ( runtimePolicy.sandboxPolicy !== undefined && isClaudeRuntimeReadOnlyFullAccessSandboxPolicy(runtimePolicy.sandboxPolicy) @@ -1411,7 +1388,7 @@ function readOnlyPolicyAllowsGlobalReads(runtimePolicy: ProviderAdapterV2Runtime } function permissionModeForClaudeRuntimePolicy( - runtimePolicy: ProviderAdapterV2RuntimePolicy, + runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy, ): PermissionMode { if (runtimePolicy.interactionMode === "plan") { return "plan"; @@ -1468,7 +1445,7 @@ export interface ClaudeRuntimeQueryPolicy { } export function claudeRuntimeQueryPolicyForRuntimePolicy( - runtimePolicy: ProviderAdapterV2RuntimePolicy, + runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy, ): ClaudeRuntimeQueryPolicy { const permissionMode = permissionModeForClaudeRuntimePolicy(runtimePolicy); const readOnlyTools = @@ -2255,7 +2232,9 @@ function isClaudeActiveSteeringAbortResult(message: SDKResultMessage): boolean { ); } -function isClaudeProviderContinuationTurn(input: ProviderAdapterV2TurnInput): boolean { +function isClaudeProviderContinuationTurn( + input: ProviderAdapter.ProviderAdapterV2TurnInput, +): boolean { return input.message.createdBy === "agent" && input.message.creationSource === "provider"; } @@ -2368,8 +2347,8 @@ function providerFailureFromApiRetry(message: SDKAPIRetryMessage): Orchestration } function buildAssistantArtifacts(input: { - readonly idAllocator: IdAllocatorV2Shape; - readonly turnInput: ProviderAdapterV2TurnInput; + readonly idAllocator: IdAllocator.IdAllocatorV2Shape; + readonly turnInput: ProviderAdapter.ProviderAdapterV2TurnInput; readonly providerTurnId: OrchestrationV2ProviderTurn["id"]; readonly nativeItemId: string; readonly text: string; @@ -2504,7 +2483,7 @@ function formatClaudeUsageLimitWait(waitMs: number): string { } interface ActiveClaudeTurnContext { - readonly input: ProviderAdapterV2TurnInput; + readonly input: ProviderAdapter.ProviderAdapterV2TurnInput; readonly nativeTurnId: string; nativeMessageCursor: string | null; readonly providerTurnId: OrchestrationV2ProviderTurn["id"]; @@ -2594,7 +2573,7 @@ interface ActiveClaudeToolCall { readonly classification: ClaudeToolClassification; readonly input: ClaudeNativeToolInput; readonly threadId: ThreadId; - readonly runId: ProviderAdapterV2TurnInput["runId"] | null; + readonly runId: ProviderAdapter.ProviderAdapterV2TurnInput["runId"] | null; readonly rootNodeId: OrchestrationV2ExecutionNode["id"]; readonly parentNodeId: OrchestrationV2ExecutionNode["id"]; readonly ordinal: number; @@ -2761,19 +2740,21 @@ export interface ClaudeAdapterV2Options { readonly attachmentsDir: string; readonly fileSystem: FileSystem.FileSystem; readonly path: Path.Path; - readonly idAllocator: IdAllocatorV2Shape; + readonly idAllocator: IdAllocator.IdAllocatorV2Shape; readonly queryRunner: ClaudeAgentSdkQueryRunnerShape; readonly scopedLimitNames?: Ref.Ref; readonly onUsageLimits?: ServerProviderShape["applyUsageLimits"]; /** Sink for wake-turn continuation requests; defaults to dropping them. */ readonly continuationRequests?: { - readonly offer: (request: ProviderContinuationRequest) => Effect.Effect; + readonly offer: ( + request: ProviderContinuationRequests.ProviderContinuationRequest, + ) => Effect.Effect; }; } export function makeClaudeAdapterV2( adapterOptions: ClaudeAdapterV2Options, -): ProviderAdapterV2Shape { +): ProviderAdapter.ProviderAdapterV2Shape { const { attachmentsDir, fileSystem, path, idAllocator, queryRunner } = adapterOptions; const continuationRequests = adapterOptions.continuationRequests ?? { offer: () => Effect.void, @@ -2801,13 +2782,13 @@ export function makeClaudeAdapterV2( Effect.provideService(Path.Path, path), ); - return ProviderAdapterV2.of({ + return ProviderAdapter.ProviderAdapterV2.of({ instanceId: adapterOptions.instanceId, driver: CLAUDE_PROVIDER, getCapabilities: () => Effect.succeed(ClaudeProviderCapabilitiesV2), planSelectionTransition: () => Effect.succeed(turnScopedSelectionTransition()), openSession: Effect.fn("ClaudeAdapterV2.openSession")( - function* (input: ProviderAdapterV2OpenSessionInput) { + function* (input: ProviderAdapter.ProviderAdapterV2OpenSessionInput) { const sessionScope = yield* Effect.scope; const now = yield* DateTime.now; const session = providerSession({ @@ -2817,7 +2798,7 @@ export function makeClaudeAdapterV2( model: input.modelSelection.model, now, }); - const events = yield* Queue.unbounded(); + const events = yield* Queue.unbounded(); const activeTurn = yield* Ref.make(null); const interruptedTurns = yield* Ref.make(new Set()); const steeredTurns = yield* Ref.make(new Set()); @@ -3054,7 +3035,7 @@ export function makeClaudeAdapterV2( const runtimeContext = yield* Effect.context(); const runPromise = Effect.runPromiseWith(runtimeContext); - const emitProviderEvent = (event: ProviderAdapterV2Event) => + const emitProviderEvent = (event: ProviderAdapter.ProviderAdapterV2Event) => Queue.offer(events, event).pipe(Effect.asVoid); // Claude emits retry progress but no recovered frame; the next @@ -3529,7 +3510,7 @@ export function makeClaudeAdapterV2( readonly classification: ClaudeToolClassification; readonly toolInput: ClaudeNativeToolInput; readonly threadId: ThreadId; - readonly runId: ProviderAdapterV2TurnInput["runId"] | null; + readonly runId: ProviderAdapter.ProviderAdapterV2TurnInput["runId"] | null; readonly rootNodeId: OrchestrationV2ExecutionNode["id"]; readonly parentNodeId: OrchestrationV2ExecutionNode["id"]; readonly ordinal: number; @@ -4394,7 +4375,7 @@ export function makeClaudeAdapterV2( const nodeId = idAllocator.derive.approvalNode({ requestId }); const providerSessionId = input.context.input.providerThread.providerSessionId; if (providerSessionId === null) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Provider thread ${input.context.input.providerThread.id} is missing a provider session id.`, }); @@ -4637,7 +4618,7 @@ export function makeClaudeAdapterV2( } const threadDisposition = input.threadDisposition ?? "reusable"; - const terminalEvent: ProviderAdapterV2Event = + const terminalEvent: ProviderAdapter.ProviderAdapterV2Event = input.status === "failed" ? { type: "turn.terminal", @@ -6516,7 +6497,7 @@ export function makeClaudeAdapterV2( ); const openQuery = Effect.fnUntraced(function* ( - turnInput: ProviderAdapterV2TurnInput, + turnInput: ProviderAdapter.ProviderAdapterV2TurnInput, nativeThreadId: string, ) { const queryPolicy = claudeRuntimeQueryPolicyForRuntimePolicy(turnInput.runtimePolicy); @@ -6672,7 +6653,7 @@ export function makeClaudeAdapterV2( }); const startTurn = Effect.fn("ClaudeAdapterV2.startTurn")( - function* (turnInput: ProviderAdapterV2TurnInput) { + function* (turnInput: ProviderAdapter.ProviderAdapterV2TurnInput) { const startedAt = yield* DateTime.now; const nativeThreadId = yield* getNativeThreadId(turnInput.providerThread); const nativeTurnId = `turn:${turnInput.attemptId}`; @@ -6683,7 +6664,7 @@ export function makeClaudeAdapterV2( const providerTurnOrdinal = turnInput.providerTurnOrdinal; const currentTurn = yield* Ref.get(activeTurn); if (currentTurn !== null) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Claude provider turn ${currentTurn.providerTurnId} is still active.`, }); @@ -6832,7 +6813,7 @@ export function makeClaudeAdapterV2( effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterTurnStartError({ + new ProviderAdapter.ProviderAdapterTurnStartError({ driver: CLAUDE_PROVIDER, threadId: turnInput.threadId, providerThreadId: turnInput.providerThread.id, @@ -6844,10 +6825,10 @@ export function makeClaudeAdapterV2( ); const interruptTurn = Effect.fn("ClaudeAdapterV2.interruptTurn")( - function* (turnInput: ProviderAdapterV2InterruptInput) { + function* (turnInput: ProviderAdapter.ProviderAdapterV2InterruptInput) { const existing = yield* Ref.get(queryContext); if (existing === null) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Claude provider thread ${turnInput.providerThread.id} has no live query.`, }); @@ -6875,7 +6856,7 @@ export function makeClaudeAdapterV2( return; } if (currentTurn?.providerTurnId !== turnInput.providerTurnId) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Claude provider turn ${turnInput.providerTurnId} is not the active turn.`, }); @@ -6914,7 +6895,7 @@ export function makeClaudeAdapterV2( effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterInterruptError({ + new ProviderAdapter.ProviderAdapterInterruptError({ driver: CLAUDE_PROVIDER, providerThreadId: turnInput.providerThread.id, providerTurnId: turnInput.providerTurnId, @@ -6925,17 +6906,17 @@ export function makeClaudeAdapterV2( ); const steerTurn = Effect.fn("ClaudeAdapterV2.steerTurn")( - function* (turnInput: ProviderAdapterV2SteerInput) { + function* (turnInput: ProviderAdapter.ProviderAdapterV2SteerInput) { const existing = yield* Ref.get(queryContext); if (existing === null) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Claude provider thread ${turnInput.providerThread.id} has no live query.`, }); } const currentTurn = yield* Ref.get(activeTurn); if (currentTurn?.providerTurnId !== turnInput.providerTurnId) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Claude provider turn ${turnInput.providerTurnId} is not the active turn.`, }); @@ -6962,7 +6943,7 @@ export function makeClaudeAdapterV2( effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterSteerRunError({ + new ProviderAdapter.ProviderAdapterSteerRunError({ driver: CLAUDE_PROVIDER, providerThreadId: turnInput.providerThread.id, providerTurnId: turnInput.providerTurnId, @@ -7013,7 +6994,7 @@ export function makeClaudeAdapterV2( }); yield* Effect.addFinalizer(() => closeSession()); - const runtime: ProviderAdapterV2SessionRuntime = { + const runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime = { instanceId: adapterOptions.instanceId, driver: CLAUDE_PROVIDER, providerSessionId: input.providerSessionId, @@ -7064,7 +7045,7 @@ export function makeClaudeAdapterV2( ); }), ensureThread: Effect.fn("ClaudeAdapterV2.ensureThread")( - function* (threadInput: ProviderAdapterV2EnsureThreadInput) { + function* (threadInput: ProviderAdapter.ProviderAdapterV2EnsureThreadInput) { const createdAt = yield* DateTime.now; const nativeThreadId = yield* queryRunner.allocateSessionId; return makeProviderThread({ @@ -7080,7 +7061,7 @@ export function makeClaudeAdapterV2( effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterEnsureThreadError({ + new ProviderAdapter.ProviderAdapterEnsureThreadError({ driver: CLAUDE_PROVIDER, threadId: threadInput.threadId, cause, @@ -7102,7 +7083,7 @@ export function makeClaudeAdapterV2( effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterResumeThreadError({ + new ProviderAdapter.ProviderAdapterResumeThreadError({ driver: CLAUDE_PROVIDER, providerSessionId: input.providerSessionId, providerThreadId: threadInput.providerThread.id, @@ -7125,10 +7106,10 @@ export function makeClaudeAdapterV2( String(requestInput.requestId), ); if (pending === undefined) { - return yield* new ProviderAdapterRuntimeRequestResponseError({ + return yield* new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({ driver: CLAUDE_PROVIDER, requestId: requestInput.requestId, - cause: new ProviderAdapterProtocolError({ + cause: new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `No pending Claude runtime request ${requestInput.requestId}.`, }), @@ -7139,10 +7120,10 @@ export function makeClaudeAdapterV2( return; } if (requestInput.decision === undefined) { - return yield* new ProviderAdapterRuntimeRequestResponseError({ + return yield* new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({ driver: CLAUDE_PROVIDER, requestId: requestInput.requestId, - cause: new ProviderAdapterProtocolError({ + cause: new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Claude ${pending.requestKind} request ${requestInput.requestId} requires an approval decision.`, }), @@ -7155,7 +7136,7 @@ export function makeClaudeAdapterV2( Effect.mapError((cause) => isProviderAdapterRuntimeRequestResponseError(cause) ? cause - : new ProviderAdapterRuntimeRequestResponseError({ + : new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({ driver: CLAUDE_PROVIDER, requestId: requestInput.requestId, cause, @@ -7165,7 +7146,7 @@ export function makeClaudeAdapterV2( ), readThreadSnapshot: (snapshotInput) => Effect.fail( - new ProviderAdapterReadThreadSnapshotError({ + new ProviderAdapter.ProviderAdapterReadThreadSnapshotError({ driver: CLAUDE_PROVIDER, providerThreadId: snapshotInput.providerThread.id, cause: "Claude V2 adapter does not implement snapshots.", @@ -7175,7 +7156,7 @@ export function makeClaudeAdapterV2( function* (rollbackInput) { const currentTurn = yield* Ref.get(activeTurn); if (currentTurn !== null) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Cannot roll back Claude provider thread ${rollbackInput.providerThread.id} while provider turn ${currentTurn.providerTurnId} is active.`, }); @@ -7238,7 +7219,7 @@ export function makeClaudeAdapterV2( Effect.mapError((cause) => isProviderAdapterRollbackThreadError(cause) ? cause - : new ProviderAdapterRollbackThreadError({ + : new ProviderAdapter.ProviderAdapterRollbackThreadError({ driver: CLAUDE_PROVIDER, providerThreadId: rollbackInput.providerThread.id, cause, @@ -7250,7 +7231,7 @@ export function makeClaudeAdapterV2( function* (forkInput) { const currentTurn = yield* Ref.get(activeTurn); if (currentTurn !== null) { - return yield* new ProviderAdapterProtocolError({ + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ driver: CLAUDE_PROVIDER, detail: `Cannot fork Claude provider thread ${forkInput.sourceProviderThread.id} while provider turn ${currentTurn.providerTurnId} is active.`, }); @@ -7295,7 +7276,7 @@ export function makeClaudeAdapterV2( effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterForkThreadError({ + new ProviderAdapter.ProviderAdapterForkThreadError({ driver: CLAUDE_PROVIDER, providerThreadId: forkInput.sourceProviderThread.id, cause, @@ -7311,7 +7292,7 @@ export function makeClaudeAdapterV2( effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterOpenSessionError({ + new ProviderAdapter.ProviderAdapterOpenSessionError({ driver: CLAUDE_PROVIDER, providerSessionId: input.providerSessionId, cause, @@ -7325,9 +7306,9 @@ export function makeClaudeAdapterV2( export type ClaudeAdapterV2DriverEnv = | ClaudeAgentSdkQueryRunner | FileSystem.FileSystem - | IdAllocatorV2 + | IdAllocator.IdAllocatorV2 | Path.Path - | ServerConfig; + | ServerConfig.ServerConfig; export const createClaudeAdapterV2 = Effect.fn("ClaudeAdapterV2Driver.create")( function* ( @@ -7337,10 +7318,10 @@ export const createClaudeAdapterV2 = Effect.fn("ClaudeAdapterV2Driver.create")( const { instanceId, environment, enabled, config } = input; const fileSystem = yield* FileSystem.FileSystem; const hostEnvironment = yield* HostProcessEnvironment; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const queryRunner = yield* ClaudeAgentSdkQueryRunner; - const serverConfig = yield* ServerConfig; - const continuationRequests = yield* ProviderContinuationRequests; + const serverConfig = yield* ServerConfig.ServerConfig; + const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests; const baseEnvironment = mergeProviderInstanceEnvironment(environment, hostEnvironment); const claudeEnvironment = yield* makeClaudeEnvironment(config, baseEnvironment); const path = yield* Path.Path; @@ -7385,10 +7366,10 @@ const makeDefaultClaudeAdapterV2 = Effect.fn("ClaudeAdapterV2.layer")(function* const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const hostEnvironment = yield* HostProcessEnvironment; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const queryRunner = yield* ClaudeAgentSdkQueryRunner; - const serverConfig = yield* ServerConfig; - const continuationRequests = yield* ProviderContinuationRequests; + const serverConfig = yield* ServerConfig.ServerConfig; + const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests; return makeClaudeAdapterV2({ instanceId: CLAUDE_DEFAULT_INSTANCE_ID, @@ -7404,7 +7385,11 @@ const makeDefaultClaudeAdapterV2 = Effect.fn("ClaudeAdapterV2.layer")(function* }); const layer: Layer.Layer< - ProviderAdapterV2, + ProviderAdapter.ProviderAdapterV2, never, - ClaudeAgentSdkQueryRunner | FileSystem.FileSystem | IdAllocatorV2 | Path.Path | ServerConfig -> = Layer.effect(ProviderAdapterV2, makeDefaultClaudeAdapterV2()); + | ClaudeAgentSdkQueryRunner + | FileSystem.FileSystem + | IdAllocator.IdAllocatorV2 + | Path.Path + | ServerConfig.ServerConfig +> = Layer.effect(ProviderAdapter.ProviderAdapterV2, makeDefaultClaudeAdapterV2()); diff --git a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts index c648869913bc..b27309f2e81e 100644 --- a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts @@ -49,16 +49,13 @@ import { TestClock } from "effect/testing"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import packageJson from "../../../package.json" with { type: "json" }; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import * as McpProviderSession from "../../mcp/McpProviderSession.ts"; import type { EventNdjsonLogger } from "../../provider/Layers/EventNdjsonLogger.ts"; -import { - NoOpProviderEventLoggers, - ProviderEventLoggers, -} from "../../provider/Layers/ProviderEventLoggers.ts"; -import { layer as idAllocatorLayer, IdAllocatorV2 } from "../IdAllocator.ts"; -import { OrchestrationEffectWorkerV2 } from "../EffectWorker.ts"; -import { OrchestratorV2 } from "../Orchestrator.ts"; +import * as ProviderEventLoggers from "../../provider/Layers/ProviderEventLoggers.ts"; +import * as IdAllocator from "../IdAllocator.ts"; +import * as EffectWorker from "../EffectWorker.ts"; +import * as Orchestrator from "../Orchestrator.ts"; import { makeOrchestratorV2ReplayLayerWithRegistry } from "../testkit/ProviderReplayHarness.ts"; import { ProviderAdapterForkThreadError, @@ -69,28 +66,7 @@ import { type ProviderAdapterV2TurnInput, } from "../ProviderAdapter.ts"; import type { ProviderContinuationRequest } from "../ProviderContinuationRequests.ts"; -import { - buildCodexTurnStartParams, - canReuseCodexContextUsage, - CODEX_DEFAULT_INSTANCE_ID, - CODEX_DRIVER_KIND, - CODEX_THREAD_CONFIG, - codexBackgroundCommandDetail, - codexFileChangeApprovalPrompt, - codexProviderTurnTokenUsage, - codexSkillMentionText, - codexThreadRuntimeParams, - CodexAppServerClientFactory, - codexAppServerClientFactoryFromSettingsLayer, - type CodexAppServerClientFactoryShape, - createCodexAdapterV2, - makeCodexAdapterV2, - makeCodexAppServerProtocolLogger, - makeCodexAppServerSpawnCommand, - projectCodexDynamicToolItem, - resolveCodexForkBoundary, - resolveCodexRollbackTurnCount, -} from "./CodexAdapterV2.ts"; +import * as CodexAdapterV2 from "./CodexAdapterV2.ts"; import { makeReplayServerConfig, makeCodexProviderAdapterRegistryReplayLayer, @@ -110,10 +86,10 @@ describe("Codex context usage compatibility", () => { }; it("retains measured usage for reasoning-only changes in either direction", () => { const low: ModelSelection = { ...previous, options: [{ id: "reasoningEffort", value: "low" }] }; - assert.isTrue(canReuseCodexContextUsage(previous, low)); - assert.isTrue(canReuseCodexContextUsage(low, previous)); + assert.isTrue(CodexAdapterV2.canReuseCodexContextUsage(previous, low)); + assert.isTrue(CodexAdapterV2.canReuseCodexContextUsage(low, previous)); assert.isTrue( - canReuseCodexContextUsage(low, { + CodexAdapterV2.canReuseCodexContextUsage(low, { ...low, options: [{ id: "reasoningEffort", value: "high" }], }), @@ -126,7 +102,7 @@ describe("Codex context usage compatibility", () => { { ...previous, options: [{ id: "contextWindow", value: "32k" }] }, { ...previous, options: [{ id: "customOption", value: "value" }] }, ]) - assert.isFalse(canReuseCodexContextUsage(previous, next)); + assert.isFalse(CodexAdapterV2.canReuseCodexContextUsage(previous, next)); }); }); @@ -142,26 +118,33 @@ describe("CodexAdapterV2 file change approvals", () => { }, }; assert.equal( - codexFileChangeApprovalPrompt({ reason: " Update configuration. ", fileChanges }), + CodexAdapterV2.codexFileChangeApprovalPrompt({ + reason: " Update configuration. ", + fileChanges, + }), "Update configuration.", ); assert.equal( - codexFileChangeApprovalPrompt({ reason: " ", fileChanges }), + CodexAdapterV2.codexFileChangeApprovalPrompt({ reason: " ", fileChanges }), "add /tmp/added.ts\nupdate /tmp/moved.ts -> /tmp/renamed.ts\ndelete /tmp/removed.md", ); }); it("falls back to a nonblank grant root and omits empty details", () => { assert.equal( - codexFileChangeApprovalPrompt({ reason: " ", grantRoot: " /workspace " }), + CodexAdapterV2.codexFileChangeApprovalPrompt({ reason: " ", grantRoot: " /workspace " }), "/workspace", ); assert.equal( - codexFileChangeApprovalPrompt({ fileChanges: {}, grantRoot: "/workspace" }), + CodexAdapterV2.codexFileChangeApprovalPrompt({ fileChanges: {}, grantRoot: "/workspace" }), "/workspace", ); assert.isUndefined( - codexFileChangeApprovalPrompt({ reason: " ", grantRoot: " ", fileChanges: {} }), + CodexAdapterV2.codexFileChangeApprovalPrompt({ + reason: " ", + grantRoot: " ", + fileChanges: {}, + }), ); }); @@ -172,7 +155,7 @@ describe("CodexAdapterV2 file change approvals", () => { { type: "add" as const, content: "" }, ]), ); - const detail = codexFileChangeApprovalPrompt({ fileChanges }); + const detail = CodexAdapterV2.codexFileChangeApprovalPrompt({ fileChanges }); assert.equal(detail?.split("\n").length, 21); assert.isTrue(detail?.startsWith("add /tmp/file-00.ts") ?? false); assert.isTrue(detail?.endsWith("+5 more") ?? false); @@ -182,7 +165,7 @@ describe("CodexAdapterV2 file change approvals", () => { describe("CodexAdapterV2 context usage", () => { it("uses the current context rather than cumulative processed tokens", () => { - const usage = codexProviderTurnTokenUsage( + const usage = CodexAdapterV2.codexProviderTurnTokenUsage( { total: { totalTokens: 180_000, @@ -422,7 +405,7 @@ describe("CodexAdapterV2 runtime policy", () => { const build = ( runtimeMode: "approval-required" | "auto-accept-edits" | "auto" | "full-access", ) => - buildCodexTurnStartParams({ + CodexAdapterV2.buildCodexTurnStartParams({ nativeThreadId: `native-${runtimeMode}`, codexInput: [{ type: "text", text: "test" }], runtimePolicy: { @@ -458,7 +441,7 @@ describe("CodexAdapterV2 runtime policy", () => { it.effect("preserves explicit Codex turn policy overrides", () => Effect.gen(function* () { - const params = yield* buildCodexTurnStartParams({ + const params = yield* CodexAdapterV2.buildCodexTurnStartParams({ nativeThreadId: "native-override", codexInput: [{ type: "text", text: "test" }], runtimePolicy: { @@ -483,7 +466,7 @@ describe("CodexAdapterV2 runtime policy", () => { it.effect("adds default-mode developer instructions when the T3 MCP server is attached", () => Effect.gen(function* () { - const params = yield* buildCodexTurnStartParams({ + const params = yield* CodexAdapterV2.buildCodexTurnStartParams({ nativeThreadId: "native-orchestration-instructions", codexInput: [{ type: "text", text: "delegate this task" }], runtimePolicy: { @@ -512,7 +495,7 @@ describe("CodexAdapterV2 runtime policy", () => { it.effect("omits default-mode collaboration settings without the T3 MCP server", () => Effect.gen(function* () { - const params = yield* buildCodexTurnStartParams({ + const params = yield* CodexAdapterV2.buildCodexTurnStartParams({ nativeThreadId: "native-default-without-t3-mcp", codexInput: [{ type: "text", text: "implement this task" }], runtimePolicy: { @@ -533,7 +516,7 @@ describe("CodexAdapterV2 runtime policy", () => { it.effect("adds T3 plan-mode developer instructions when the T3 MCP server is attached", () => Effect.gen(function* () { - const params = yield* buildCodexTurnStartParams({ + const params = yield* CodexAdapterV2.buildCodexTurnStartParams({ nativeThreadId: "native-plan-with-t3-mcp", codexInput: [{ type: "text", text: "plan this task" }], runtimePolicy: { @@ -559,7 +542,7 @@ describe("CodexAdapterV2 runtime policy", () => { it.effect("keeps Codex in plan mode without referencing unavailable T3 MCP tools", () => Effect.gen(function* () { - const params = yield* buildCodexTurnStartParams({ + const params = yield* CodexAdapterV2.buildCodexTurnStartParams({ nativeThreadId: "native-plan-without-t3-mcp", codexInput: [{ type: "text", text: "plan this task" }], runtimePolicy: { @@ -581,7 +564,7 @@ describe("CodexAdapterV2 runtime policy", () => { it.effect("compiles per-turn Codex model options and cwd from their owning inputs", () => Effect.gen(function* () { - const params = yield* buildCodexTurnStartParams({ + const params = yield* CodexAdapterV2.buildCodexTurnStartParams({ nativeThreadId: "native-model-options", codexInput: [{ type: "text", text: "test" }], runtimePolicy: { @@ -608,7 +591,7 @@ describe("CodexAdapterV2 runtime policy", () => { assert.equal(params.collaborationMode?.settings.reasoning_effort, "xhigh"); // ChatGPT token sharing rejects service tiers, so managed sessions drop a stale pick. - const managed = yield* buildCodexTurnStartParams({ + const managed = yield* CodexAdapterV2.buildCodexTurnStartParams({ nativeThreadId: "native-model-options", codexInput: [{ type: "text", text: "test" }], runtimePolicy: { @@ -643,7 +626,7 @@ describe("CodexAdapterV2 process spawning", () => { try { assert.deepEqual( - codexThreadRuntimeParams({ + CodexAdapterV2.codexThreadRuntimeParams({ threadId, modelSelection: { model: "gpt-5.4" }, runtimePolicy: { @@ -675,7 +658,7 @@ describe("CodexAdapterV2 process spawning", () => { it.effect("resolves Windows command shims through the shared spawn policy", () => Effect.gen(function* () { - const command = yield* makeCodexAppServerSpawnCommand({ + const command = yield* CodexAdapterV2.makeCodexAppServerSpawnCommand({ command: "codex", args: ["app-server", "argument with spaces"], cwd: "C:\\workspace", @@ -709,7 +692,7 @@ describe("CodexAdapterV2 process spawning", () => { it.effect("uses direct execution for native executables", () => Effect.gen(function* () { - const command = yield* makeCodexAppServerSpawnCommand({ + const command = yield* CodexAdapterV2.makeCodexAppServerSpawnCommand({ command: "codex.exe", args: ["app-server"], }); @@ -736,15 +719,18 @@ describe("CodexAdapterV2 process spawning", () => { PlatformError.systemError({ _tag: "NotFound", module: "ChildProcess", method: "spawn" }), ); }); - const factory = yield* CodexAppServerClientFactory.pipe( - Effect.provide(codexAppServerClientFactoryFromSettingsLayer), + const factory = yield* CodexAdapterV2.CodexAppServerClientFactory.pipe( + Effect.provide(CodexAdapterV2.codexAppServerClientFactoryFromSettingsLayer), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), - Effect.provideService(ProviderEventLoggers, NoOpProviderEventLoggers), + Effect.provideService( + ProviderEventLoggers.ProviderEventLoggers, + ProviderEventLoggers.NoOpProviderEventLoggers, + ), ); const open = (environment: NodeJS.ProcessEnv) => factory .open({ - instanceId: CODEX_DEFAULT_INSTANCE_ID, + instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID, threadId: ThreadId.make("thread-launch-args"), providerSessionId: ProviderSessionId.make("provider-session-launch-args"), runtimePolicy: ProviderAdapterV2RuntimePolicy.make({ @@ -780,8 +766,8 @@ describe("CodexAdapterV2 process spawning", () => { ); }); const path = yield* Path.Path; - const adapter = yield* createCodexAdapterV2({ - instanceId: CODEX_DEFAULT_INSTANCE_ID, + const adapter = yield* CodexAdapterV2.createCodexAdapterV2({ + instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID, displayName: undefined, environment: [], enabled: true, @@ -789,12 +775,15 @@ describe("CodexAdapterV2 process spawning", () => { }).pipe( Effect.provide( Layer.mergeAll( - codexAppServerClientFactoryFromSettingsLayer, + CodexAdapterV2.codexAppServerClientFactoryFromSettingsLayer, ServerConfig.layerTest(process.cwd(), { prefix: "t3-codex-binary-home-" }), ), ), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), - Effect.provideService(ProviderEventLoggers, NoOpProviderEventLoggers), + Effect.provideService( + ProviderEventLoggers.ProviderEventLoggers, + ProviderEventLoggers.NoOpProviderEventLoggers, + ), ); yield* adapter @@ -808,7 +797,7 @@ describe("CodexAdapterV2 process spawning", () => { assert.deepEqual(spawnedCommands, [path.join(NodeOS.homedir(), "bin", "codex")]); }).pipe( - Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer)), + Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer)), Effect.provideService(HostProcessPlatform, "linux"), ), ); @@ -828,16 +817,22 @@ describe("CodexAdapterV2 dynamic tool projection", () => { }, result: { content: [] }, }; - assert.equal(projectCodexDynamicToolItem(call).title, "Inspect Saga music screen"); assert.equal( - projectCodexDynamicToolItem({ ...call, arguments: { title: " " } }).title, + CodexAdapterV2.projectCodexDynamicToolItem(call).title, + "Inspect Saga music screen", + ); + assert.equal( + CodexAdapterV2.projectCodexDynamicToolItem({ ...call, arguments: { title: " " } }).title, + undefined, + ); + assert.equal( + CodexAdapterV2.projectCodexDynamicToolItem({ ...call, server: "github" }).title, undefined, ); - assert.equal(projectCodexDynamicToolItem({ ...call, server: "github" }).title, undefined); }); it("preserves native browser and app icons alongside MCP tool output", () => { - const browser = projectCodexDynamicToolItem({ + const browser = CodexAdapterV2.projectCodexDynamicToolItem({ type: "mcpToolCall", id: "browser", server: "browser", @@ -865,7 +860,7 @@ describe("CodexAdapterV2 dynamic tool projection", () => { faviconUrl: "https://example.com/icon.png", }); assert.equal(browser.toolSource?.name, "Chrome"); - const app = projectCodexDynamicToolItem({ + const app = CodexAdapterV2.projectCodexDynamicToolItem({ type: "mcpToolCall", id: "app", server: "computer", @@ -890,7 +885,7 @@ describe("CodexAdapterV2 dynamic tool projection", () => { }); it("preserves MCP arguments and prefers structured output", () => { - const projection = projectCodexDynamicToolItem({ + const projection = CodexAdapterV2.projectCodexDynamicToolItem({ type: "mcpToolCall", id: "call-create-threads", server: "t3-code", @@ -920,7 +915,7 @@ describe("CodexAdapterV2 dynamic tool projection", () => { }); it("preserves namespaced dynamic tool output", () => { - const projection = projectCodexDynamicToolItem({ + const projection = CodexAdapterV2.projectCodexDynamicToolItem({ type: "dynamicToolCall", id: "call-dynamic", namespace: "workspace", @@ -957,7 +952,7 @@ describe("CodexAdapterV2 native protocol logging", () => { }; const threadId = ThreadId.make("thread-1"); const providerSessionId = ProviderSessionId.make("provider-session-1"); - const protocolLogger = makeCodexAppServerProtocolLogger({ + const protocolLogger = CodexAdapterV2.makeCodexAppServerProtocolLogger({ nativeEventLogger: logger, threadId, providerSessionId, @@ -1013,7 +1008,7 @@ describe("CodexAdapterV2 native protocol logging", () => { it.effect("filters streaming frames before redaction without losing decode failures", () => Effect.gen(function* () { const writes: Array = []; - const protocolLogger = makeCodexAppServerProtocolLogger({ + const protocolLogger = CodexAdapterV2.makeCodexAppServerProtocolLogger({ nativeEventLogger: { filePath: "/tmp/events.log", write: (event) => @@ -1060,7 +1055,7 @@ describe("CodexAdapterV2 native protocol logging", () => { it.effect("retains redacted failures when large payloads are summarized", () => Effect.gen(function* () { const writes: Array = []; - const protocolLogger = makeCodexAppServerProtocolLogger({ + const protocolLogger = CodexAdapterV2.makeCodexAppServerProtocolLogger({ nativeEventLogger: { filePath: "/tmp/events.log", write: (event) => @@ -1110,13 +1105,13 @@ describe("CodexAdapterV2 rollback mapping", () => { const providerThreadId = ProviderThreadId.make("provider-thread-codex-rollback"); const providerThread: OrchestrationV2ProviderThread = { id: providerThreadId, - driver: CODEX_DRIVER_KIND, + driver: CodexAdapterV2.CODEX_DRIVER_KIND, providerInstanceId: ProviderInstanceId.make("codex"), providerSessionId: ProviderSessionId.make("provider-session-codex-rollback"), appThreadId: ThreadId.make("thread-codex-rollback"), ownerNodeId: null, nativeThreadRef: { - driver: CODEX_DRIVER_KIND, + driver: CodexAdapterV2.CODEX_DRIVER_KIND, nativeId: "native-thread-codex-rollback", strength: "strong", }, @@ -1139,7 +1134,7 @@ describe("CodexAdapterV2 rollback mapping", () => { nodeId: NodeId.make(`node-${id}`), runAttemptId: RunAttemptId.make(`run-attempt-${id}`), nativeTurnRef: { - driver: CODEX_DRIVER_KIND, + driver: CodexAdapterV2.CODEX_DRIVER_KIND, nativeId: `native-${id}`, strength: "strong", }, @@ -1153,7 +1148,7 @@ describe("CodexAdapterV2 rollback mapping", () => { const runningTurn = providerTurn("provider-turn-running", 3, "running"); const interruptedTurn = providerTurn("provider-turn-interrupted", 4, "interrupted"); - const numTurns = yield* resolveCodexRollbackTurnCount({ + const numTurns = yield* CodexAdapterV2.resolveCodexRollbackTurnCount({ providerThread, target: { type: "provider_turn", @@ -1173,13 +1168,13 @@ describe("CodexAdapterV2 fork boundary", () => { const providerThreadId = ProviderThreadId.make("provider-thread-codex-fork-boundary"); const makeProviderThread = (now: DateTime.Utc): OrchestrationV2ProviderThread => ({ id: providerThreadId, - driver: CODEX_DRIVER_KIND, + driver: CodexAdapterV2.CODEX_DRIVER_KIND, providerInstanceId: ProviderInstanceId.make("codex"), providerSessionId: ProviderSessionId.make("provider-session-codex-fork-boundary"), appThreadId: ThreadId.make("thread-codex-fork-boundary"), ownerNodeId: null, nativeThreadRef: { - driver: CODEX_DRIVER_KIND, + driver: CodexAdapterV2.CODEX_DRIVER_KIND, nativeId: "native-thread-codex-fork-boundary", strength: "strong", }, @@ -1204,8 +1199,8 @@ describe("CodexAdapterV2 fork boundary", () => { runAttemptId: RunAttemptId.make(`run-attempt-${id}`), nativeTurnRef: nativeId === null - ? { driver: CODEX_DRIVER_KIND, nativeId: null, strength: "none" } - : { driver: CODEX_DRIVER_KIND, nativeId, strength: "strong" }, + ? { driver: CodexAdapterV2.CODEX_DRIVER_KIND, nativeId: null, strength: "none" } + : { driver: CodexAdapterV2.CODEX_DRIVER_KIND, nativeId, strength: "strong" }, ordinal, status: "completed", startedAt: now, @@ -1218,7 +1213,7 @@ describe("CodexAdapterV2 fork boundary", () => { const firstTurn = makeProviderTurn("provider-turn-first", 1, "native-turn-first", now); const secondTurn = makeProviderTurn("provider-turn-second", 2, "native-turn-second", now); - const boundary = yield* resolveCodexForkBoundary({ + const boundary = yield* CodexAdapterV2.resolveCodexForkBoundary({ sourceProviderThread: makeProviderThread(now), sourceProviderTurns: [firstTurn, secondTurn], providerTurnId: firstTurn.id, @@ -1238,7 +1233,7 @@ describe("CodexAdapterV2 fork boundary", () => { const firstTurn = makeProviderTurn("provider-turn-first", 1, "native-turn-first", now); const secondTurn = makeProviderTurn("provider-turn-second", 2, "native-turn-second", now); - const boundary = yield* resolveCodexForkBoundary({ + const boundary = yield* CodexAdapterV2.resolveCodexForkBoundary({ sourceProviderThread: makeProviderThread(now), sourceProviderTurns: [firstTurn, secondTurn], providerTurnId: secondTurn.id, @@ -1258,7 +1253,7 @@ describe("CodexAdapterV2 fork boundary", () => { const firstTurn = makeProviderTurn("provider-turn-first", 1, null, now); const secondTurn = makeProviderTurn("provider-turn-second", 2, "native-turn-second", now); - const boundary = yield* resolveCodexForkBoundary({ + const boundary = yield* CodexAdapterV2.resolveCodexForkBoundary({ sourceProviderThread: makeProviderThread(now), sourceProviderTurns: [firstTurn, secondTurn], providerTurnId: firstTurn.id, @@ -1278,7 +1273,7 @@ describe("CodexAdapterV2 fork boundary", () => { }; const secondTurn = makeProviderTurn("provider-turn-second", 2, "native-turn-second", now); - const boundary = yield* resolveCodexForkBoundary({ + const boundary = yield* CodexAdapterV2.resolveCodexForkBoundary({ sourceProviderThread: makeProviderThread(now), sourceProviderTurns: [firstTurn, secondTurn], providerTurnId: firstTurn.id, @@ -1293,7 +1288,7 @@ describe("CodexAdapterV2 fork boundary", () => { Effect.gen(function* () { const now = yield* DateTime.now; - const boundary = yield* resolveCodexForkBoundary({ + const boundary = yield* CodexAdapterV2.resolveCodexForkBoundary({ sourceProviderThread: makeProviderThread(now), targetThreadId: ThreadId.make("thread-codex-fork-target"), }); @@ -1308,7 +1303,7 @@ describe("CodexAdapterV2 fork boundary", () => { const firstTurn = makeProviderTurn("provider-turn-first", 1, "native-turn-first", now); const error = yield* Effect.flip( - resolveCodexForkBoundary({ + CodexAdapterV2.resolveCodexForkBoundary({ sourceProviderThread: makeProviderThread(now), sourceProviderTurns: [firstTurn], providerTurnId: ProviderTurnId.make("provider-turn-missing"), @@ -1337,7 +1332,7 @@ describe("CodexAdapterV2 skill mentions", () => { ["5€review", "5€review"], ]; for (const [text, expected] of cases) { - assert.equal(codexSkillMentionText(text), expected, text); + assert.equal(CodexAdapterV2.codexSkillMentionText(text), expected, text); } }); }); @@ -1345,7 +1340,7 @@ describe("CodexAdapterV2 skill mentions", () => { describe("CodexAdapterV2 background command detail", () => { it("summarizes command, exit code, and output tail", () => { assert.equal( - codexBackgroundCommandDetail({ + CodexAdapterV2.codexBackgroundCommandDetail({ command: "sleep 20 && echo CODEX_BG_WAKE_DONE", exitCode: 0, aggregatedOutput: "CODEX_BG_WAKE_DONE\n", @@ -1357,7 +1352,7 @@ describe("CodexAdapterV2 background command detail", () => { it("omits the output section and exit code when absent", () => { assert.equal( - codexBackgroundCommandDetail({ + CodexAdapterV2.codexBackgroundCommandDetail({ command: "sleep 20", exitCode: null, aggregatedOutput: null, @@ -1367,7 +1362,7 @@ describe("CodexAdapterV2 background command detail", () => { }); it("truncates long commands and keeps only the output tail", () => { - const detail = codexBackgroundCommandDetail({ + const detail = CodexAdapterV2.codexBackgroundCommandDetail({ command: "x".repeat(300), exitCode: 1, aggregatedOutput: `${"y".repeat(2000)}TAIL`, @@ -1381,7 +1376,7 @@ describe("CodexAdapterV2 background command detail", () => { const DEFAULT_CODEX_SETTINGS = Schema.decodeSync(CodexSettings)({}); const CODEX_TEST_MODEL_SELECTION = { - instanceId: CODEX_DEFAULT_INSTANCE_ID, + instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID, model: "gpt-5.4", } satisfies ModelSelection; const CODEX_TEST_RUNTIME_POLICY = ProviderAdapterV2RuntimePolicy.make({ @@ -1401,7 +1396,7 @@ function makeCodexTestAppThread(input: { id: input.threadId, projectId: ProjectId.make(`project-${input.threadId}`), title: "Codex continuation test", - providerInstanceId: CODEX_DEFAULT_INSTANCE_ID, + providerInstanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID, modelSelection: CODEX_TEST_MODEL_SELECTION, runtimeMode: "full-access", interactionMode: "default", @@ -1510,7 +1505,11 @@ function codexReplayPreamble(input: { { type: "expect_outbound", label: "thread/start", - frame: { id: 2, method: "thread/start", params: { config: CODEX_THREAD_CONFIG } }, + frame: { + id: 2, + method: "thread/start", + params: { config: CodexAdapterV2.CODEX_THREAD_CONFIG }, + }, }, { type: "emit_inbound", @@ -1624,16 +1623,16 @@ describe("CodexAdapterV2 post-settle continuation", () => { ) => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const serverConfig = yield* makeReplayServerConfig(transcript.scenario).pipe(Effect.orDie); const continuationRequests: Array = []; - const clientFactory: CodexAppServerClientFactoryShape = { + const clientFactory: CodexAdapterV2.CodexAppServerClientFactoryShape = { open: (openInput) => Layer.build(CodexReplay.layerReplay(transcript)).pipe( Effect.mapError( (cause) => new ProviderAdapterOpenSessionError({ - driver: CODEX_DRIVER_KIND, + driver: CodexAdapterV2.CODEX_DRIVER_KIND, providerSessionId: openInput.providerSessionId, cause, }), @@ -1658,8 +1657,8 @@ describe("CodexAdapterV2 post-settle continuation", () => { ), ), }; - const adapter = makeCodexAdapterV2({ - instanceId: CODEX_DEFAULT_INSTANCE_ID, + const adapter = CodexAdapterV2.makeCodexAdapterV2({ + instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID, settings: DEFAULT_CODEX_SETTINGS, environment: {}, clientFactory, @@ -1830,7 +1829,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { ); assert.equal(requests.filter((method) => method === "turn/start").length, 1); assert.isBelow(requests.indexOf("thread/inject_items"), requests.indexOf("turn/start")); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); } @@ -1864,7 +1863,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { }, }, ]); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect("unsubscribes from the native thread when it is unloaded", () => @@ -1902,7 +1901,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.isDefined(harness.runtime.unloadThread); yield* harness.runtime.unloadThread!({ providerThread: harness.providerThread }); assert.deepEqual(requests, ["initialize", "thread/start", "thread/unsubscribe"]); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect("keeps the app-server failure as the cause when an unload is rejected", () => @@ -1936,7 +1935,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.equal(error._tag, "ProviderAdapterProtocolError"); const cause = error._tag === "ProviderAdapterProtocolError" ? error.cause : undefined; assert.equal((cause as { _tag?: string } | undefined)?._tag, "CodexAppServerRequestError"); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect("waits for native start before interrupting an acknowledged queued turn", () => @@ -2015,8 +2014,8 @@ describe("CodexAdapterV2 post-settle continuation", () => { text: prompt, }), ); - const providerTurnId = (yield* IdAllocatorV2).derive.providerTurn({ - driver: CODEX_DRIVER_KIND, + const providerTurnId = (yield* IdAllocator.IdAllocatorV2).derive.providerTurn({ + driver: CodexAdapterV2.CODEX_DRIVER_KIND, nativeTurnId, }); const interrupt = yield* harness.runtime @@ -2030,7 +2029,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.equal(harness.terminalEvents()[0]?.status, "interrupted"); assert.lengthOf(harness.terminalEvents(), 1); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect("settles Stop when a queued native turn fails before starting", () => @@ -2096,8 +2095,8 @@ describe("CodexAdapterV2 post-settle continuation", () => { text: prompt, }), ); - const providerTurnId = (yield* IdAllocatorV2).derive.providerTurn({ - driver: CODEX_DRIVER_KIND, + const providerTurnId = (yield* IdAllocator.IdAllocatorV2).derive.providerTurn({ + driver: CodexAdapterV2.CODEX_DRIVER_KIND, nativeTurnId, }); const interrupt = yield* harness.runtime @@ -2112,7 +2111,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.lengthOf(harness.terminalEvents(), 1); assert.isFalse(interruptSent, "A terminal native turn must not receive turn/interrupt"); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect("bounds Stop when a queued native turn never starts", () => @@ -2158,8 +2157,8 @@ describe("CodexAdapterV2 post-settle continuation", () => { text: prompt, }), ); - const providerTurnId = (yield* IdAllocatorV2).derive.providerTurn({ - driver: CODEX_DRIVER_KIND, + const providerTurnId = (yield* IdAllocator.IdAllocatorV2).derive.providerTurn({ + driver: CodexAdapterV2.CODEX_DRIVER_KIND, nativeTurnId, }); const interrupt = yield* harness.runtime @@ -2172,7 +2171,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.lengthOf(harness.terminalEvents(), 1); assert.isFalse(interruptSent, "An unstarted native turn must not receive turn/interrupt"); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect("sends currency-sigil skill mentions to Codex as $ mentions", () => @@ -2221,8 +2220,8 @@ describe("CodexAdapterV2 post-settle continuation", () => { threadId: harness.threadId, runId: turnInput.runId, providerThread: harness.providerThread, - providerTurnId: (yield* IdAllocatorV2).derive.providerTurn({ - driver: CODEX_DRIVER_KIND, + providerTurnId: (yield* IdAllocator.IdAllocatorV2).derive.providerTurn({ + driver: CodexAdapterV2.CODEX_DRIVER_KIND, nativeTurnId, }), message: { @@ -2231,7 +2230,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { text: "then £ship it", }, }); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); const assistantMessages = (events: ReadonlyArray) => @@ -2373,7 +2372,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { }); } assert.isEmpty(assistantMessages(harness.events)); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -2382,7 +2381,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { Effect.gen(function* () { const nativeThreadId = "context-thread"; const nativeTurnId = "context-turn"; - const params = yield* buildCodexTurnStartParams({ + const params = yield* CodexAdapterV2.buildCodexTurnStartParams({ nativeThreadId, codexInput: [{ type: "text", text: "work" }], runtimePolicy: CODEX_TEST_RUNTIME_POLICY, @@ -2470,7 +2469,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { ); yield* harness.firstTerminal; assert.equal(harness.terminalEvents()[0]?.status, "completed"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -2546,7 +2545,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { ); assert.equal(items[0]?.id, items[1]?.id); assert.equal(harness.terminalEvents()[0]?.status, "completed"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -2573,7 +2572,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { params: { threadId: nativeThreadId, excludeTurns: true, - config: CODEX_THREAD_CONFIG, + config: CodexAdapterV2.CODEX_THREAD_CONFIG, }, }, }, @@ -2594,7 +2593,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.equal(resumed.nativeThreadRef?.nativeId, nativeThreadId); assert.equal(resumed.status, "idle"); assert.equal(DateTime.toEpochMillis(resumed.updatedAt), 1782622450000); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -2622,7 +2621,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { params: { threadId: nativeThreadId, excludeTurns: true, - config: CODEX_THREAD_CONFIG, + config: CodexAdapterV2.CODEX_THREAD_CONFIG, }, }, }, @@ -2676,7 +2675,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { restartContinuationOfRunId: RunId.make("run-before-restart"), }); assert.equal(resumed.nativeThreadRef?.nativeId, nativeThreadId); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -2824,7 +2823,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.isAtLeast(recoveredIndex, 0); assert.isAbove(resumedCommandIndex, recoveredIndex); assert.isAbove(terminalIndex, resumedCommandIndex); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -2923,7 +2922,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { ["compaction", 1782622565000], ], ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3086,7 +3085,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { ); } assert.deepEqual(assistantMessages(harness.events), []); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); } @@ -3216,7 +3215,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assistantMessages(harness.events).map((event) => event.message.text), ["CODEX_RECOVERY_OK"], ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3260,7 +3259,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assistantMessages(harness.events).map((event) => event.message.text), ["CODEX_RECOVERY_OK"], ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3382,7 +3381,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { new Set(assistantMessages(harness.events).map((event) => event.message.id)).size, 1, ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3410,7 +3409,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assistantMessages(harness.events).map((event) => event.message.text), [""], ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3439,7 +3438,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assistantMessages(harness.events).map((event) => event.message.text), [""], ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3468,7 +3467,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assistantMessages(harness.events).map((event) => event.message.text), ["Working on it.", ""], ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3497,7 +3496,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assistantMessages(harness.events).map((event) => event.message.text), ["CODEX_RECOVERY_OK"], ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3526,7 +3525,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assistantMessages(harness.events).map((event) => event.message.text), ["CODEX_RECOVERY_OK"], ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3555,7 +3554,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assistantMessages(harness.events).map((event) => event.message.text), ["", "CODEX_RECOVERY_OK"], ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3682,7 +3681,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { const request = harness.continuationRequests[0]; assert.equal(request?.threadId, harness.threadId); assert.equal(request?.providerThreadId, harness.providerThread.id); - assert.equal(request?.driver, CODEX_DRIVER_KIND); + assert.equal(request?.driver, CodexAdapterV2.CODEX_DRIVER_KIND); assert.deepEqual(request?.notification, { source: { kind: "command" }, outcome: "completed", @@ -3711,7 +3710,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { ); assert.lengthOf(harness.terminalEvents(), 1); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -3852,7 +3851,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.lengthOf(harness.terminalEvents(), 1); assert.equal(harness.terminalEvents()[0]?.status, "completed"); assert.lengthOf(harness.continuationRequests, 0); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); if (terminated === true) { @@ -3898,8 +3897,8 @@ describe("CodexAdapterV2 post-settle continuation", () => { 0, ); yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const worker = yield* OrchestrationEffectWorkerV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; const threadId = ThreadId.make("thread:background-stop"); yield* orchestrator.dispatch({ type: "thread.create", @@ -4095,7 +4094,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.lengthOf(harness.continuationRequests, 0); assert.isFalse(yield* harness.hasPendingBackgroundWork); assert.lengthOf(harness.terminalEvents(), 1); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4376,7 +4375,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { ); assert.isAtLeast(commandUpdates.length, 2, "start + interrupt terminalization"); assert.equal(commandUpdates[commandUpdates.length - 1]?.turnItem.status, "interrupted"); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4595,7 +4594,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.equal(harness.subagentUpdates().at(-1)?.subagent.status, "interrupted"); assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4676,7 +4675,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted root terminal"); assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4760,7 +4759,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted root terminal"); assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -4862,7 +4861,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.equal(lateChildUpdates.at(-1)?.providerTurn.status, "interrupted"); assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5023,7 +5022,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { ); assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5280,7 +5279,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { commandUpdatesBeforeLateEvents.length, "late starts and completions must not project after timeout", ); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5383,7 +5382,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted terminal"); assert.equal(harness.terminalEvents()[0]?.status, "interrupted"); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5620,7 +5619,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.isDefined(item); } if (scenario.name === "retry") assert.equal(terminal.retry?.attempt, 1); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); } @@ -5719,7 +5718,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { ); assert.isFalse(yield* harness.hasPendingBackgroundWork); assert.lengthOf(harness.continuationRequests, 0); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -5961,7 +5960,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { ); assert.lengthOf(harness.terminalEvents(), 1); assert.isFalse(yield* harness.hasPendingBackgroundWork); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -6173,7 +6172,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { yield* TestClock.adjust("100 millis"); yield* harness.firstTerminal; assert.equal(harness.subagentUpdates().at(-1)?.subagent.model, model); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -6246,7 +6245,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { yield* Deferred.succeed(releaseMetadata, undefined); yield* TestClock.adjust("30 seconds"); assert.equal(harness.subagentUpdates().at(-1)?.subagent.model, model); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -6308,7 +6307,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.isFalse(yield* harness.hasPendingBackgroundWork); assert.lengthOf(harness.terminalEvents(), 1); assert.lengthOf(harness.continuationRequests, 0); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -6388,7 +6387,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.isNull(row?.completedAt); assert.isNotNull(row?.startedAt); assert.equal(DateTime.toEpochMillis(row!.startedAt!), 1782622470000); - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); @@ -6549,7 +6548,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { DateTime.toEpochMillis(first.subagent.completedAt!), ); } - }).pipe(Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ), ); } @@ -6606,8 +6605,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { runAttemptId: RunAttemptId.make(`run-attempt-${input.id}`), nativeTurnRef: input.nativeId === null - ? { driver: CODEX_DRIVER_KIND, nativeId: null, strength: "none" } - : { driver: CODEX_DRIVER_KIND, nativeId: input.nativeId, strength: "strong" }, + ? { driver: CodexAdapterV2.CODEX_DRIVER_KIND, nativeId: null, strength: "none" } + : { + driver: CodexAdapterV2.CODEX_DRIVER_KIND, + nativeId: input.nativeId, + strength: "strong", + }, ordinal: input.ordinal, status: "completed", startedAt: input.now, @@ -6694,7 +6697,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { "thread/rollback", "thread/rollback must not be sent to a legacy Codex thread", ); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect( @@ -6718,7 +6721,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { frame: { id: 3, method: "thread/fork", - params: { threadId: nativeThreadId, config: CODEX_THREAD_CONFIG }, + params: { threadId: nativeThreadId, config: CodexAdapterV2.CODEX_THREAD_CONFIG }, }, }, { @@ -6833,7 +6836,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.notEqual(forkedProviderThread.id, harness.providerThread.id); assert.equal(forkedProviderThread.forkedFrom?.providerTurnId, firstTurn.id); assert.deepEqual(outbound.slice(-2), ["thread/turns/list", "thread/revert"]); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect( @@ -6857,7 +6860,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { frame: { id: 3, method: "thread/fork", - params: { threadId: nativeThreadId, config: CODEX_THREAD_CONFIG }, + params: { threadId: nativeThreadId, config: CodexAdapterV2.CODEX_THREAD_CONFIG }, }, }, { @@ -6935,7 +6938,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { "thread/rollback", "thread/rollback must not be sent to a legacy Codex fork", ); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); it.effect("propagates native thread/fork failures as typed fork errors", () => @@ -6959,7 +6962,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { params: { threadId: nativeThreadId, lastTurnId: "native-turn-first", - config: CODEX_THREAD_CONFIG, + config: CodexAdapterV2.CODEX_THREAD_CONFIG, }, }, }, @@ -6991,6 +6994,6 @@ describe("CodexAdapterV2 post-settle continuation", () => { assert.instanceOf(error, ProviderAdapterForkThreadError); assert.include(errorCauseChainText(error), "fork exploded"); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), ); }); diff --git a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.testkit.ts index 6745e9953c8a..d449c1a51f6b 100644 --- a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.testkit.ts @@ -12,19 +12,14 @@ import * as PlatformError from "effect/PlatformError"; import * as Predicate from "effect/Predicate"; import * as Schema from "effect/Schema"; -import { ServerConfig } from "../../config.ts"; -import { layer as idAllocatorLayer } from "../IdAllocator.ts"; +import * as ServerConfig from "../../config.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { ProviderAdapterOpenSessionError } from "../ProviderAdapter.ts"; import { ProviderAdapterDriverCreateError } from "../ProviderAdapterDriver.ts"; -import { makeDriverLayer as makeProviderAdapterRegistryDriverLayer } from "../ProviderAdapterRegistry.ts"; +import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts"; import type { ProviderReplayGate } from "../testkit/ProviderReplayGate.testkit.ts"; -import { - CODEX_DEFAULT_INSTANCE_ID, - CODEX_DRIVER_KIND, - CodexAdapterV2Driver, - CodexAppServerClientFactory, -} from "./CodexAdapterV2.ts"; +import * as CodexAdapterV2 from "./CodexAdapterV2.ts"; export class CodexReplayTranscriptDecodeError extends Schema.TaggedError()( "CodexReplayTranscriptDecodeError", @@ -98,7 +93,7 @@ function metadataFromTranscript(transcript: ProviderReplayTranscript): { export function makeReplayServerConfig( scenario: string, ): Effect.Effect< - ServerConfig["Service"], + ServerConfig.ServerConfig["Service"], PlatformError.PlatformError, FileSystem.FileSystem | Path.Path > { @@ -190,14 +185,14 @@ export function makeCodexProviderAdapterRegistryReplayLayer(input: { input.driver === undefined ? CodexReplay.layerReplay(input.transcript) : CodexReplay.layerReplayWithDriver(input.driver); - const replayClientFactoryLayer = Layer.succeed(CodexAppServerClientFactory, { + const replayClientFactoryLayer = Layer.succeed(CodexAdapterV2.CodexAppServerClientFactory, { open: (openInput) => Effect.gen(function* () { const context = yield* Layer.build(replayLayer).pipe( Effect.mapError( (cause) => new ProviderAdapterOpenSessionError({ - driver: CODEX_DRIVER_KIND, + driver: CodexAdapterV2.CODEX_DRIVER_KIND, providerSessionId: openInput.providerSessionId, cause, }), @@ -210,14 +205,14 @@ export function makeCodexProviderAdapterRegistryReplayLayer(input: { }), }); const serverConfigLayer = Layer.effect( - ServerConfig, + ServerConfig.ServerConfig, makeReplayServerConfig(input.transcript.scenario).pipe(Effect.orDie), ).pipe(Layer.provide(NodeServices.layer)); - const registryLayer = makeProviderAdapterRegistryDriverLayer({ - drivers: [CodexAdapterV2Driver], + const registryLayer = ProviderAdapterRegistry.makeDriverLayer({ + drivers: [CodexAdapterV2.CodexAdapterV2Driver], configMap: { - [CODEX_DEFAULT_INSTANCE_ID]: { - driver: CODEX_DRIVER_KIND, + [CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID]: { + driver: CodexAdapterV2.CODEX_DRIVER_KIND, }, }, }).pipe( @@ -226,7 +221,7 @@ export function makeCodexProviderAdapterRegistryReplayLayer(input: { replayClientFactoryLayer, serverConfigLayer, NodeServices.layer, - idAllocatorLayer, + IdAllocator.layer, ), ), ); @@ -242,7 +237,7 @@ export const CodexOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarness CodexReplay.CodexAppServerReplayTranscript, CodexOrchestratorReplayHarnessError > = { - driver: CODEX_DRIVER_KIND, + driver: CodexAdapterV2.CODEX_DRIVER_KIND, decodeTranscript: (transcript) => decodeCodexAppServerReplayTranscript(transcript).pipe( Effect.mapError( diff --git a/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.test.ts index 7d51f3793a7e..d31e91eac08c 100644 --- a/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.test.ts @@ -22,9 +22,9 @@ import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; -import { ServerConfig, layerTest as serverConfigLayerTest } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import * as McpProviderSession from "../../mcp/McpProviderSession.ts"; -import { IdAllocatorV2, layer as idAllocatorLayer } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { ProviderAdapterV2RuntimePolicy } from "../ProviderAdapter.ts"; import { cursorMcpServers, @@ -65,10 +65,10 @@ describe("CursorAdapterV2", () => { environment: { HOME: workspace }, fileSystem, path, - idAllocator: yield* IdAllocatorV2, - serverConfig: yield* ServerConfig.pipe( + idAllocator: yield* IdAllocator.IdAllocatorV2, + serverConfig: yield* ServerConfig.ServerConfig.pipe( Effect.provide( - serverConfigLayerTest(workspace, { prefix: "cursor-v2-lifecycle-config-" }), + ServerConfig.layerTest(workspace, { prefix: "cursor-v2-lifecycle-config-" }), ), ), runner: { @@ -184,7 +184,7 @@ describe("CursorAdapterV2", () => { status === "finished" ? "idle" : status === "cancelled" ? "cancelled" : "failed", ); assert.isNotNull(rows.at(-1)?.subagent.completedAt); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(NodeServices.layer, idAllocatorLayer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(NodeServices.layer, IdAllocator.layer))), ); } @@ -209,9 +209,9 @@ describe("CursorAdapterV2", () => { environment: { HOME: workspace }, fileSystem, path, - idAllocator: yield* IdAllocatorV2, - serverConfig: yield* ServerConfig.pipe( - Effect.provide(serverConfigLayerTest(workspace, { prefix: "cursor-v2-error-config-" })), + idAllocator: yield* IdAllocator.IdAllocatorV2, + serverConfig: yield* ServerConfig.ServerConfig.pipe( + Effect.provide(ServerConfig.layerTest(workspace, { prefix: "cursor-v2-error-config-" })), ), runner: { assertComplete: Effect.void, @@ -335,7 +335,7 @@ describe("CursorAdapterV2", () => { Stream.runHead, ); assert.equal(sentMessages[1], "/compress"); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(NodeServices.layer, idAllocatorLayer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(NodeServices.layer, IdAllocator.layer))), ); it.effect("projects Cursor directory trees and lint diagnostics as file search results", () => @@ -514,9 +514,9 @@ describe("CursorAdapterV2", () => { environment: { HOME: workspace }, fileSystem, path, - idAllocator: yield* IdAllocatorV2, - serverConfig: yield* ServerConfig.pipe( - Effect.provide(serverConfigLayerTest(workspace, { prefix: "cursor-v2-search-config-" })), + idAllocator: yield* IdAllocator.IdAllocatorV2, + serverConfig: yield* ServerConfig.ServerConfig.pipe( + Effect.provide(ServerConfig.layerTest(workspace, { prefix: "cursor-v2-search-config-" })), ), runner: { assertComplete: Effect.void, @@ -671,7 +671,7 @@ describe("CursorAdapterV2", () => { }, ], ); - }).pipe(Effect.scoped, Effect.provide(Layer.merge(NodeServices.layer, idAllocatorLayer))), + }).pipe(Effect.scoped, Effect.provide(Layer.merge(NodeServices.layer, IdAllocator.layer))), ); it("maps Cursor auto and model parameters to SDK selections", () => { diff --git a/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.testkit.ts index fa94b74c5c7d..51bf5c3b154a 100644 --- a/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.testkit.ts @@ -19,27 +19,12 @@ import * as Path from "effect/Path"; import * as PlatformError from "effect/PlatformError"; import * as Schema from "effect/Schema"; -import { ServerConfig } from "../../config.ts"; -import { layer as idAllocatorLayer } from "../IdAllocator.ts"; +import * as ServerConfig from "../../config.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { ProviderAdapterDriverCreateError } from "../ProviderAdapterDriver.ts"; -import { makeDriverLayer as makeProviderAdapterRegistryDriverLayer } from "../ProviderAdapterRegistry.ts"; +import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts"; -import { - CURSOR_AGENT_SDK_PROTOCOL, - CURSOR_PROVIDER, - CursorAgentSdkRunner, - CursorAgentSdkRunnerError, - isCursorCancellationError, - loggedCursorAgentOptions, - loggedCursorSendOptions, - makeCursorAgentSdkRunner, - type CursorAgentSdkOpenInput, - type CursorAgentSdkProtocolLogEvent, - type CursorAgentSdkRun, - type CursorAgentSdkRunnerShape, - type CursorAgentSdkSendInput, - type CursorAgentSdkSession, -} from "./CursorAgentSdk.ts"; +import * as CursorAgentSdk from "./CursorAgentSdk.ts"; import { CURSOR_DEFAULT_INSTANCE_ID, CURSOR_DRIVER_KIND, @@ -51,8 +36,8 @@ import type { ProviderAdapterV2RuntimePolicy } from "../ProviderAdapter.ts"; import type { RuntimePolicyV2Override } from "../RuntimePolicy.ts"; const CursorAgentSdkReplayTranscript = Schema.Struct({ - provider: Schema.Literal(CURSOR_PROVIDER), - protocol: Schema.Literal(CURSOR_AGENT_SDK_PROTOCOL), + provider: Schema.Literal(CursorAgentSdk.CURSOR_PROVIDER), + protocol: Schema.Literal(CursorAgentSdk.CURSOR_AGENT_SDK_PROTOCOL), version: Schema.String, scenario: Schema.String, metadata: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)), @@ -139,7 +124,7 @@ export const CursorAgentSdkReplayError = Schema.Union([ ]); export type CursorAgentSdkReplayError = typeof CursorAgentSdkReplayError.Type; const isCursorAgentSdkReplayError = Schema.is(CursorAgentSdkReplayError); -const isCursorAgentSdkRunnerError = Schema.is(CursorAgentSdkRunnerError); +const isCursorAgentSdkRunnerError = Schema.is(CursorAgentSdk.CursorAgentSdkRunnerError); export const CursorOrchestratorReplayHarnessError = Schema.Union([ CursorAgentSdkReplayError, @@ -147,9 +132,9 @@ export const CursorOrchestratorReplayHarnessError = Schema.Union([ ]); export type CursorOrchestratorReplayHarnessError = typeof CursorOrchestratorReplayHarnessError.Type; -type CursorProtocolPayload = CursorAgentSdkProtocolLogEvent["payload"]; +type CursorProtocolPayload = CursorAgentSdk.CursorAgentSdkProtocolLogEvent["payload"]; type CursorOutgoingFrame = Extract< - CursorAgentSdkProtocolLogEvent, + CursorAgentSdk.CursorAgentSdkProtocolLogEvent, { readonly direction: "outgoing" } >["payload"]; @@ -214,11 +199,11 @@ function replayRunnerError( transcript: CursorAgentSdkReplayTranscript, cause: unknown, method: string, -): CursorAgentSdkRunnerError { +): CursorAgentSdk.CursorAgentSdkRunnerError { if (isCursorAgentSdkRunnerError(cause)) { return cause; } - return new CursorAgentSdkRunnerError({ + return new CursorAgentSdk.CursorAgentSdkRunnerError({ method, cause: isCursorAgentSdkReplayError(cause) ? cause @@ -232,7 +217,7 @@ function replayRunnerError( export function makeCursorAgentSdkReplayRunner( transcript: CursorAgentSdkReplayTranscript, -): CursorAgentSdkRunnerShape { +): CursorAgentSdk.CursorAgentSdkRunnerShape { let cursor = 0; let failure: CursorAgentSdkReplayError | null = null; let cursorAdvanced = makeSignal(); @@ -308,7 +293,7 @@ export function makeCursorAgentSdkReplayRunner( const waitForRun = ( runId: string, - sendInput: CursorAgentSdkSendInput, + sendInput: CursorAgentSdk.CursorAgentSdkSendInput, ): Effect.Effect => Effect.gen(function* () { while (true) { @@ -424,17 +409,17 @@ export function makeCursorAgentSdkReplayRunner( }); return { - open: (input: CursorAgentSdkOpenInput) => + open: (input: CursorAgentSdk.CursorAgentSdkOpenInput) => Effect.try({ try: () => { assertOutbound({ type: "agent.open", operation: input.operation, ...(input.agentId === undefined ? {} : { agentId: input.agentId }), - options: loggedCursorAgentOptions(input.options), + options: CursorAgentSdk.loggedCursorAgentOptions(input.options), }); const opened = consumeInbound("agent.opened"); - const session: CursorAgentSdkSession = { + const session: CursorAgentSdk.CursorAgentSdkSession = { agentId: opened.agentId, send: (sendInput) => Effect.try({ @@ -442,10 +427,10 @@ export function makeCursorAgentSdkReplayRunner( assertOutbound({ type: "run.start", message: sendInput.message, - options: loggedCursorSendOptions(sendInput.options), + options: CursorAgentSdk.loggedCursorSendOptions(sendInput.options), }); const started = consumeInbound("run.started"); - const run: CursorAgentSdkRun = { + const run: CursorAgentSdk.CursorAgentSdkRun = { runId: started.runId, agentId: started.agentId, wait: waitForRun(started.runId, sendInput).pipe( @@ -510,20 +495,20 @@ export function makeCursorAgentSdkReplayRunner( function makeCursorAgentSdkReplayLayer( transcript: CursorAgentSdkReplayTranscript, options?: { - readonly runner?: CursorAgentSdkRunnerShape; + readonly runner?: CursorAgentSdk.CursorAgentSdkRunnerShape; readonly assertCompleteOnFinalize?: boolean; }, -): Layer.Layer { +): Layer.Layer { const runner = options?.runner ?? makeCursorAgentSdkReplayRunner(transcript); return Layer.effect( - CursorAgentSdkRunner, + CursorAgentSdk.CursorAgentSdkRunner, Effect.gen(function* () { yield* Effect.addFinalizer(() => options?.assertCompleteOnFinalize === false ? Effect.void : runner.assertComplete.pipe(Effect.orDie), ); - return CursorAgentSdkRunner.of(runner); + return CursorAgentSdk.CursorAgentSdkRunner.of(runner); }), ); } @@ -531,7 +516,7 @@ function makeCursorAgentSdkReplayLayer( function makeReplayServerConfig( scenario: string, ): Effect.Effect< - ServerConfig["Service"], + ServerConfig.ServerConfig["Service"], PlatformError.PlatformError, FileSystem.FileSystem | Path.Path > { @@ -616,12 +601,12 @@ function makeReplayServerConfig( export function makeCursorProviderAdapterRegistryReplayLayer( transcript: CursorAgentSdkReplayTranscript, options?: { - readonly runner?: CursorAgentSdkRunnerShape; + readonly runner?: CursorAgentSdk.CursorAgentSdkRunnerShape; readonly assertCompleteOnFinalize?: boolean; }, ) { const serverConfigLayer = Layer.effect( - ServerConfig, + ServerConfig.ServerConfig, makeReplayServerConfig(transcript.scenario).pipe(Effect.orDie), ).pipe(Layer.provide(NodeServices.layer)); // Skill discovery also scans user roots under HOME; an empty HOME keeps @@ -634,7 +619,7 @@ export function makeCursorProviderAdapterRegistryReplayLayer( return { HOME: home }; }).pipe(Effect.orDie), ).pipe(Layer.provide(NodeServices.layer)); - return makeProviderAdapterRegistryDriverLayer({ + return ProviderAdapterRegistry.makeDriverLayer({ drivers: [CursorAdapterV2Driver], configMap: { [CURSOR_DEFAULT_INSTANCE_ID]: { @@ -648,7 +633,7 @@ export function makeCursorProviderAdapterRegistryReplayLayer( serverConfigLayer, hostEnvironmentLayer, NodeServices.layer, - idAllocatorLayer, + IdAllocator.layer, ), ), ); @@ -670,7 +655,7 @@ export const CursorOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarnes CursorAgentSdkReplayTranscript, CursorOrchestratorReplayHarnessError > = { - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, decodeTranscript: (transcript) => decodeCursorAgentSdkReplayTranscript(transcript).pipe( Effect.mapError( @@ -883,7 +868,7 @@ export const recordCursorAgentSdkReplayTranscript = Effect.fn( let awaitingToolStart = input.interruptAfterToolStart === true; let heldUntilCancel: Array | undefined; - const recordFrame = (event: CursorAgentSdkProtocolLogEvent) => + const recordFrame = (event: CursorAgentSdk.CursorAgentSdkProtocolLogEvent) => Effect.sync(() => { const frame = event.payload; if (frame.type === "agent.open") { @@ -915,7 +900,7 @@ export const recordCursorAgentSdkReplayTranscript = Effect.fn( heldUntilCancel = []; } }); - const runner = makeCursorAgentSdkRunner(() => recordFrame); + const runner = CursorAgentSdk.makeCursorAgentSdkRunner(() => recordFrame); const awaitSignal = (signal: Deferred.Deferred, description: string) => Deferred.await(signal).pipe( @@ -926,7 +911,7 @@ export const recordCursorAgentSdkReplayTranscript = Effect.fn( ); const runPrompt = Effect.fnUntraced(function* ( - session: CursorAgentSdkSession, + session: CursorAgentSdk.CursorAgentSdkSession, prompt: string, index: number, ) { @@ -976,7 +961,7 @@ export const recordCursorAgentSdkReplayTranscript = Effect.fn( Effect.catchIf( (error) => (input.interruptAfterToolStart === true || interruptAfterRunStart) && - isCursorCancellationError(error.cause), + CursorAgentSdk.isCursorCancellationError(error.cause), (error) => Effect.sync(() => { entries.push({ @@ -989,7 +974,7 @@ export const recordCursorAgentSdkReplayTranscript = Effect.fn( ); }); - const runPrompts = (session: CursorAgentSdkSession, from: number, to: number) => + const runPrompts = (session: CursorAgentSdk.CursorAgentSdkSession, from: number, to: number) => Effect.forEach( input.prompts.slice(from, to), (prompt, offset) => runPrompt(session, prompt, from + offset), @@ -1000,7 +985,7 @@ export const recordCursorAgentSdkReplayTranscript = Effect.fn( open: | { readonly operation: "create" } | { readonly operation: "resume"; readonly agentId: string }, - use: (session: CursorAgentSdkSession) => Effect.Effect, + use: (session: CursorAgentSdk.CursorAgentSdkSession) => Effect.Effect, ) => Effect.acquireUseRelease( runner.open({ @@ -1024,8 +1009,8 @@ export const recordCursorAgentSdkReplayTranscript = Effect.fn( } return { - provider: CURSOR_PROVIDER, - protocol: CURSOR_AGENT_SDK_PROTOCOL, + provider: CursorAgentSdk.CURSOR_PROVIDER, + protocol: CursorAgentSdk.CURSOR_AGENT_SDK_PROTOCOL, version: "1", scenario: input.scenario, metadata: { diff --git a/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.ts index 4829fb0cb3c7..97ddcc7c26ab 100644 --- a/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.ts @@ -43,7 +43,7 @@ import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; import { resolveAttachmentPath } from "../../attachmentStore.ts"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import * as McpProviderSession from "../../mcp/McpProviderSession.ts"; import { CursorTransportFailure } from "../../provider/acp/CursorTransportFailure.ts"; import { cursorSdkModelSelection } from "../../provider/cursorSdkModel.ts"; @@ -55,35 +55,14 @@ import { import { mergeProviderInstanceEnvironment } from "../../provider/ProviderInstanceEnvironment.ts"; import { t3OrchestrationPromptForFirstRun } from "../../provider/T3OrchestrationInstructions.ts"; import { buildRuntimeInstructions } from "../../provider/RuntimeInstructions.ts"; -import { IdAllocatorV2, type IdAllocatorV2Shape } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { makeProviderFailure } from "../ProviderFailure.ts"; import { turnScopedSelectionTransition } from "../ProviderSelectionTransition.ts"; import { isProviderNativeImageAttachment, providerMessageTextWithAttachmentPaths, } from "../AttachmentPrompt.ts"; -import { - ProviderAdapterEnsureThreadError, - ProviderAdapterForkThreadError, - ProviderAdapterInterruptError, - ProviderAdapterOpenSessionError, - ProviderAdapterProtocolError, - ProviderAdapterReadThreadSnapshotError, - ProviderAdapterResumeThreadError, - ProviderAdapterRollbackThreadError, - ProviderAdapterRuntimeRequestResponseError, - ProviderAdapterSteerRunUnsupportedError, - ProviderAdapterTurnStartError, - ProviderAdapterV2, - type ProviderAdapterV2EnsureThreadInput, - type ProviderAdapterV2Event, - type ProviderAdapterV2InterruptInput, - type ProviderAdapterV2OpenSessionInput, - type ProviderAdapterV2RuntimePolicy, - type ProviderAdapterV2SessionRuntime, - type ProviderAdapterV2Shape, - type ProviderAdapterV2TurnInput, -} from "../ProviderAdapter.ts"; +import * as ProviderAdapter from "../ProviderAdapter.ts"; import { ProviderAdapterDriverCreateError, type ProviderAdapterDriver, @@ -94,16 +73,10 @@ import { makeSubagentConversationArtifacts, subagentThreadTitle, } from "../SubagentProjection.ts"; -import { - CURSOR_PROVIDER, - CursorAgentSdkRunner, - type CursorAgentSdkRun, - type CursorAgentSdkRunnerShape, - type CursorAgentSdkSession, -} from "./CursorAgentSdk.ts"; +import * as CursorAgentSdk from "./CursorAgentSdk.ts"; export { cursorSdkModelSelection } from "../../provider/cursorSdkModel.ts"; -export const CURSOR_DRIVER_KIND = CURSOR_PROVIDER; +export const CURSOR_DRIVER_KIND = CursorAgentSdk.CURSOR_PROVIDER; export const CURSOR_DEFAULT_INSTANCE_ID = defaultInstanceIdForDriver(CURSOR_DRIVER_KIND); const DEFAULT_CURSOR_SETTINGS = Schema.decodeSync(CursorSettings)({}); @@ -207,7 +180,7 @@ export interface CursorRuntimeAgentPolicy { } export function cursorRuntimeAgentPolicy( - runtimePolicy: ProviderAdapterV2RuntimePolicy, + runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy, ): CursorRuntimeAgentPolicy { const sandboxPolicyType = typeof runtimePolicy.sandboxPolicy === "object" && @@ -253,7 +226,7 @@ function providerSession(input: { }): OrchestrationV2ProviderSession { return { id: input.providerSessionId, - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, providerInstanceId: input.providerInstanceId, status: "ready", cwd: input.cwd ?? process.cwd(), @@ -266,7 +239,7 @@ function providerSession(input: { } function makeProviderThread(input: { - readonly idAllocator: IdAllocatorV2Shape; + readonly idAllocator: IdAllocator.IdAllocatorV2Shape; readonly providerInstanceId: ProviderInstanceId; readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"]; readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"]; @@ -277,16 +250,16 @@ function makeProviderThread(input: { }): OrchestrationV2ProviderThread { return { id: input.idAllocator.derive.providerThread({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeThreadId: input.nativeThreadId, }), - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, providerInstanceId: input.providerInstanceId, providerSessionId: input.providerSessionId, appThreadId: input.appThreadId, ownerNodeId: input.ownerNodeId ?? null, nativeThreadRef: { - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeId: input.nativeThreadId, strength: "strong", }, @@ -304,8 +277,8 @@ function makeProviderThread(input: { function nativeThreadId(providerThread: OrchestrationV2ProviderThread): string { const id = providerThread.nativeThreadRef?.nativeId; if (id === null || id === undefined) { - throw new ProviderAdapterProtocolError({ - driver: CURSOR_PROVIDER, + throw new ProviderAdapter.ProviderAdapterProtocolError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, detail: `Provider thread ${providerThread.id} is missing its Cursor agent id.`, }); } @@ -331,7 +304,7 @@ const CURSOR_AGENT_SETTING_SOURCES = [ export function makeCursorAgentOptions(input: { readonly apiKey?: string; readonly modelSelection: ModelSelection; - readonly runtimePolicy: ProviderAdapterV2RuntimePolicy; + readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy; readonly threadId: ThreadId; }): AgentOptions { const policy = cursorRuntimeAgentPolicy(input.runtimePolicy); @@ -806,7 +779,7 @@ function textFromAgentMessage(message: AgentMessage): string { interface CursorProjectionTarget { readonly threadId: ThreadId; - readonly runId: ProviderAdapterV2TurnInput["runId"] | null; + readonly runId: ProviderAdapter.ProviderAdapterV2TurnInput["runId"] | null; readonly rootNodeId: OrchestrationV2ExecutionNode["rootNodeId"]; readonly parentNodeId: OrchestrationV2ExecutionNode["id"]; readonly providerThreadId: OrchestrationV2ProviderThread["id"] | null; @@ -846,8 +819,8 @@ interface ActiveCursorTextStream { } interface ActiveCursorTurn { - readonly input: ProviderAdapterV2TurnInput; - readonly run: CursorAgentSdkRun; + readonly input: ProviderAdapter.ProviderAdapterV2TurnInput; + readonly run: CursorAgentSdk.CursorAgentSdkRun; readonly providerTurnId: OrchestrationV2ProviderTurn["id"]; readonly startedAt: DateTime.Utc; readonly completed: Deferred.Deferred; @@ -864,7 +837,7 @@ interface ActiveCursorTurn { interface CursorLiveAgent { readonly nativeThreadId: string; - readonly session: CursorAgentSdkSession; + readonly session: CursorAgentSdk.CursorAgentSdkSession; } export interface CursorAdapterV2Options { @@ -873,24 +846,24 @@ export interface CursorAdapterV2Options { readonly environment: NodeJS.ProcessEnv; readonly fileSystem: FileSystem.FileSystem; readonly path: Path.Path; - readonly idAllocator: IdAllocatorV2Shape; - readonly runner: CursorAgentSdkRunnerShape; - readonly serverConfig: ServerConfig["Service"]; + readonly idAllocator: IdAllocator.IdAllocatorV2Shape; + readonly runner: CursorAgentSdk.CursorAgentSdkRunnerShape; + readonly serverConfig: ServerConfig.ServerConfig["Service"]; } export function makeCursorAdapterV2( adapterOptions: CursorAdapterV2Options, -): ProviderAdapterV2Shape { +): ProviderAdapter.ProviderAdapterV2Shape { const { fileSystem, path, idAllocator, runner, serverConfig } = adapterOptions; const apiKey = adapterOptions.environment.CURSOR_API_KEY?.trim() || undefined; - return ProviderAdapterV2.of({ + return ProviderAdapter.ProviderAdapterV2.of({ instanceId: adapterOptions.instanceId, - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, getCapabilities: () => Effect.succeed(CursorProviderCapabilitiesV2), planSelectionTransition: () => Effect.succeed(turnScopedSelectionTransition()), openSession: Effect.fn("CursorAdapterV2.openSession")( - function* (input: ProviderAdapterV2OpenSessionInput) { + function* (input: ProviderAdapter.ProviderAdapterV2OpenSessionInput) { const sessionScope = yield* Effect.scope; const createdAt = yield* DateTime.now; const session = providerSession({ @@ -900,12 +873,12 @@ export function makeCursorAdapterV2( model: input.modelSelection.model, now: createdAt, }); - const events = yield* Queue.unbounded(); + const events = yield* Queue.unbounded(); const liveAgent = yield* Ref.make(null); const activeTurn = yield* Ref.make(null); const planIds = yield* Ref.make(new Map()); - const emitProviderEvent = (event: ProviderAdapterV2Event) => + const emitProviderEvent = (event: ProviderAdapter.ProviderAdapterV2Event) => Queue.offer(events, event).pipe(Effect.asVoid); const resolveItemOrdinal = (context: ActiveCursorTurn, nativeItemId: string) => @@ -930,7 +903,7 @@ export function makeCursorAdapterV2( const planId = yield* idAllocator.allocate.plan({ threadId: context.input.threadId, runId: context.input.runId, - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, }); yield* Ref.update(planIds, (current) => { const updated = new Map(current); @@ -960,25 +933,25 @@ export function makeCursorAdapterV2( const now = yield* DateTime.now; const ordinal = yield* resolveItemOrdinal(context, segment.nativeItemId); const nodeId = idAllocator.derive.nodeFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId: segment.nativeItemId, }); const messageId = idAllocator.derive.messageFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId: segment.nativeItemId, }); const turnItemId = idAllocator.derive.turnItemFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId: segment.nativeItemId, }); const nativeItemRef = { - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeId: segment.nativeItemId, strength: "weak" as const, }; yield* emitProviderEvent({ type: "node.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, node: { id: nodeId, threadId: context.input.threadId, @@ -999,7 +972,7 @@ export function makeCursorAdapterV2( }); yield* emitProviderEvent({ type: "message.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, message: { createdBy: "agent", creationSource: "provider", @@ -1017,7 +990,7 @@ export function makeCursorAdapterV2( }); yield* emitProviderEvent({ type: "turn_item.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, turnItem: { id: turnItemId, threadId: context.input.threadId, @@ -1052,21 +1025,21 @@ export function makeCursorAdapterV2( const now = yield* DateTime.now; const ordinal = yield* resolveItemOrdinal(context, segment.nativeItemId); const nodeId = idAllocator.derive.nodeFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId: segment.nativeItemId, }); const turnItemId = idAllocator.derive.turnItemFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId: segment.nativeItemId, }); const nativeItemRef = { - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeId: segment.nativeItemId, strength: "weak" as const, }; yield* emitProviderEvent({ type: "node.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, node: { id: nodeId, threadId: context.input.threadId, @@ -1087,7 +1060,7 @@ export function makeCursorAdapterV2( }); yield* emitProviderEvent({ type: "turn_item.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, turnItem: { id: turnItemId, threadId: context.input.threadId, @@ -1157,15 +1130,15 @@ export function makeCursorAdapterV2( ? "running" : (input.unfinishedStatus ?? (failed ? "failed" : "completed")); const nodeId = idAllocator.derive.nodeFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId: active.callId, }); const turnItemId = idAllocator.derive.turnItemFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId: active.callId, }); const nativeItemRef = { - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeId: active.callId, strength: "strong" as const, }; @@ -1303,12 +1276,12 @@ export function makeCursorAdapterV2( } yield* emitProviderEvent({ type: "node.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, node, }); yield* emitProviderEvent({ type: "turn_item.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, turnItem, }); }); @@ -1349,16 +1322,16 @@ export function makeCursorAdapterV2( const planId = yield* resolvePlanId(input.context, nativeItemId); const ordinal = yield* resolveItemOrdinal(input.context, nativeItemId); const nodeId = idAllocator.derive.nodeFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId, }); const turnItemId = idAllocator.derive.turnItemFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId, }); const status = input.failed ? "failed" : input.completed ? "completed" : "running"; const nativeItemRef = { - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeId: input.callId, strength: "strong" as const, }; @@ -1373,7 +1346,7 @@ export function makeCursorAdapterV2( }; yield* emitProviderEvent({ type: "node.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, node: { id: nodeId, threadId: input.context.input.threadId, @@ -1394,12 +1367,12 @@ export function makeCursorAdapterV2( }); yield* emitProviderEvent({ type: "plan.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, plan, }); yield* emitProviderEvent({ type: "turn_item.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, turnItem: { id: turnItemId, threadId: input.context.input.threadId, @@ -1435,23 +1408,23 @@ export function makeCursorAdapterV2( const planId = yield* resolvePlanId(input.context, nativeItemId); const ordinal = yield* resolveItemOrdinal(input.context, nativeItemId); const nodeId = idAllocator.derive.nodeFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId, }); const turnItemId = idAllocator.derive.turnItemFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId, }); const status = input.failed ? "failed" : input.completed ? "completed" : "running"; const steps = cursorTodoSteps(input.toolCall); const nativeItemRef = { - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeId: input.callId, strength: "strong" as const, }; yield* emitProviderEvent({ type: "node.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, node: { id: nodeId, threadId: input.context.input.threadId, @@ -1472,7 +1445,7 @@ export function makeCursorAdapterV2( }); yield* emitProviderEvent({ type: "plan.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, plan: { id: planId, threadId: input.context.input.threadId, @@ -1485,7 +1458,7 @@ export function makeCursorAdapterV2( }); yield* emitProviderEvent({ type: "turn_item.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, turnItem: { id: turnItemId, threadId: input.context.input.threadId, @@ -1543,19 +1516,19 @@ export function makeCursorAdapterV2( const nodeId = existing?.task.id ?? idAllocator.derive.nodeFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId, }); const childRootNodeId = existing?.childRootNodeId ?? idAllocator.derive.nodeFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId: `${nativeItemId}:child-root`, }); const childThreadId = existing?.childThreadId ?? idAllocator.derive.threadFromProviderThread({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeThreadId: `${input.context.run.runId}:task:${input.callId}`, }); const task: OrchestrationV2Subagent = { @@ -1566,12 +1539,12 @@ export function makeCursorAdapterV2( parentNodeId: input.context.input.rootNodeId, origin: "provider_native" as const, createdBy: "agent" as const, - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, providerInstanceId: input.context.input.modelSelection.instanceId, providerThreadId: null, childThreadId, nativeTaskRef: { - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeId: input.callId, strength: "strong" as const, }, @@ -1582,7 +1555,7 @@ export function makeCursorAdapterV2( startedAt: now, }), nativeTaskRef: { - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeId: input.callId, strength: "strong" as const, }, @@ -1600,7 +1573,7 @@ export function makeCursorAdapterV2( turnItemId: existing?.turnItemId ?? idAllocator.derive.turnItemFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId, }), turnItemOrdinal: @@ -1613,7 +1586,7 @@ export function makeCursorAdapterV2( if (existing === undefined) { yield* emitProviderEvent({ type: "app_thread.created", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, appThread: makeSubagentChildThread({ parentThread: input.context.input.appThread, childThreadId, @@ -1636,11 +1609,11 @@ export function makeCursorAdapterV2( const promptArtifacts = makeSubagentConversationArtifacts({ senderThreadId: input.context.input.threadId, messageId: idAllocator.derive.messageFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId: promptNativeId, }), turnItemId: idAllocator.derive.turnItemFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId: promptNativeId, }), threadId: childThreadId, @@ -1648,7 +1621,7 @@ export function makeCursorAdapterV2( providerThreadId: null, providerTurnId: null, nativeItemRef: { - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeId: promptNativeId, strength: "weak", }, @@ -1659,19 +1632,19 @@ export function makeCursorAdapterV2( }); yield* emitProviderEvent({ type: "message.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, message: promptArtifacts.message, }); yield* emitProviderEvent({ type: "turn_item.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, turnItem: promptArtifacts.turnItem, }); } yield* emitProviderEvent({ type: "node.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, node: { id: nodeId, threadId: input.context.input.threadId, @@ -1692,7 +1665,7 @@ export function makeCursorAdapterV2( }); yield* emitProviderEvent({ type: "node.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, node: { id: childRootNodeId, threadId: childThreadId, @@ -1713,12 +1686,12 @@ export function makeCursorAdapterV2( }); yield* emitProviderEvent({ type: "subagent.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, subagent: task, }); yield* emitProviderEvent({ type: "turn_item.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, turnItem: { id: subagent.turnItemId, threadId: input.context.input.threadId, @@ -1772,11 +1745,11 @@ export function makeCursorAdapterV2( const resultNativeId = `${nativeItemId}:result`; const resultArtifacts = makeSubagentConversationArtifacts({ messageId: idAllocator.derive.messageFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId: resultNativeId, }), turnItemId: idAllocator.derive.turnItemFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId: resultNativeId, }), threadId: childThreadId, @@ -1784,7 +1757,7 @@ export function makeCursorAdapterV2( providerThreadId: null, providerTurnId: null, nativeItemRef: { - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeId: resultNativeId, strength: "weak", }, @@ -1795,12 +1768,12 @@ export function makeCursorAdapterV2( }); yield* emitProviderEvent({ type: "message.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, message: resultArtifacts.message, }); yield* emitProviderEvent({ type: "turn_item.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, turnItem: resultArtifacts.turnItem, }); } @@ -1944,7 +1917,7 @@ export function makeCursorAdapterV2( nodeId: input.context.input.rootNodeId, runAttemptId: input.context.input.attemptId, nativeTurnRef: { - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeId: input.context.run.runId, strength: "strong", }, @@ -1995,7 +1968,7 @@ export function makeCursorAdapterV2( yield* completeAssistant(input.context); yield* emitProviderEvent({ type: "provider_turn.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, providerTurn: providerTurnPayload({ context: input.context, status: input.status, @@ -2004,7 +1977,7 @@ export function makeCursorAdapterV2( }); yield* emitProviderEvent({ type: "provider_thread.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, providerThread: { ...input.context.input.providerThread, providerSessionId: session.id, @@ -2021,7 +1994,7 @@ export function makeCursorAdapterV2( input.status === "failed" ? { type: "turn.terminal", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, providerThreadId: input.context.input.providerThread.id, providerTurnId: input.context.providerTurnId, runOrdinal: input.context.input.runOrdinal, @@ -2035,7 +2008,7 @@ export function makeCursorAdapterV2( } : { type: "turn.terminal", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, providerThreadId: input.context.input.providerThread.id, providerTurnId: input.context.providerTurnId, runOrdinal: input.context.input.runOrdinal, @@ -2074,7 +2047,7 @@ export function makeCursorAdapterV2( readonly operation: "create" | "resume"; readonly threadId: ThreadId; readonly modelSelection: ModelSelection; - readonly runtimePolicy: ProviderAdapterV2RuntimePolicy; + readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy; readonly agentId?: string; }) { const existing = yield* Ref.get(liveAgent); @@ -2111,7 +2084,7 @@ export function makeCursorAdapterV2( let cursorSkillNames: ReadonlySet | undefined; const resolveUserMessage = Effect.fnUntraced(function* ( - turnInput: ProviderAdapterV2TurnInput, + turnInput: ProviderAdapter.ProviderAdapterV2TurnInput, ) { const rawText = turnInput.message.text; if (rawText.trim() === "/compress" && turnInput.message.attachments.length === 0) { @@ -2152,16 +2125,16 @@ export function makeCursorAdapterV2( attachment, }); if (path === null) { - return yield* new ProviderAdapterProtocolError({ - driver: CURSOR_PROVIDER, + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, detail: `Invalid attachment id '${attachment.id}'.`, }); } const bytes = yield* fileSystem.readFile(path).pipe( Effect.mapError( (cause) => - new ProviderAdapterProtocolError({ - driver: CURSOR_PROVIDER, + new ProviderAdapter.ProviderAdapterProtocolError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, detail: `Failed to read attachment '${attachment.id}'.`, payload: cause, }), @@ -2175,8 +2148,8 @@ export function makeCursorAdapterV2( { concurrency: 1 }, ); if (userText.length === 0 && images.length === 0) { - return yield* new ProviderAdapterProtocolError({ - driver: CURSOR_PROVIDER, + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, detail: "Cursor turn requires non-empty text or attachments.", }); } @@ -2190,11 +2163,11 @@ export function makeCursorAdapterV2( }); const startTurn = Effect.fn("CursorAdapterV2.startTurn")( - function* (turnInput: ProviderAdapterV2TurnInput) { + function* (turnInput: ProviderAdapter.ProviderAdapterV2TurnInput) { const current = yield* Ref.get(activeTurn); if (current !== null) { - return yield* new ProviderAdapterProtocolError({ - driver: CURSOR_PROVIDER, + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, detail: `Cursor provider turn ${current.providerTurnId} is still active.`, }); } @@ -2229,7 +2202,7 @@ export function makeCursorAdapterV2( const startedAt = yield* DateTime.now; const completed = yield* Deferred.make(); const providerTurnId = idAllocator.derive.providerTurn({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeTurnId: sdkRun.runId, }); context = { @@ -2256,7 +2229,7 @@ export function makeCursorAdapterV2( yield* Ref.set(activeTurn, context); yield* emitProviderEvent({ type: "provider_turn.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, providerTurn: providerTurnPayload({ context, status: "running", @@ -2265,7 +2238,7 @@ export function makeCursorAdapterV2( }); yield* emitProviderEvent({ type: "provider_thread.updated", - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, providerThread: { ...turnInput.providerThread, providerSessionId: session.id, @@ -2345,8 +2318,8 @@ export function makeCursorAdapterV2( effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterTurnStartError({ - driver: CURSOR_PROVIDER, + new ProviderAdapter.ProviderAdapterTurnStartError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, threadId: turnInput.threadId, providerThreadId: turnInput.providerThread.id, runId: turnInput.runId, @@ -2373,14 +2346,14 @@ export function makeCursorAdapterV2( }); yield* Effect.addFinalizer(() => closeSession()); - const runtime: ProviderAdapterV2SessionRuntime = { + const runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime = { instanceId: adapterOptions.instanceId, - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, providerSessionId: input.providerSessionId, providerSession: session, events: Stream.fromEffectRepeat(Queue.take(events)), ensureThread: Effect.fn("CursorAdapterV2.ensureThread")( - function* (threadInput: ProviderAdapterV2EnsureThreadInput) { + function* (threadInput: ProviderAdapter.ProviderAdapterV2EnsureThreadInput) { const opened = yield* openAgent({ operation: "create", threadId: threadInput.threadId, @@ -2401,8 +2374,8 @@ export function makeCursorAdapterV2( effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterEnsureThreadError({ - driver: CURSOR_PROVIDER, + new ProviderAdapter.ProviderAdapterEnsureThreadError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, threadId: threadInput.threadId, cause, }), @@ -2431,8 +2404,8 @@ export function makeCursorAdapterV2( effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterResumeThreadError({ - driver: CURSOR_PROVIDER, + new ProviderAdapter.ProviderAdapterResumeThreadError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, providerSessionId: input.providerSessionId, providerThreadId: threadInput.providerThread.id, cause, @@ -2448,17 +2421,17 @@ export function makeCursorAdapterV2( }), steerTurn: (turnInput) => Effect.fail( - new ProviderAdapterSteerRunUnsupportedError({ - driver: CURSOR_PROVIDER, + new ProviderAdapter.ProviderAdapterSteerRunUnsupportedError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, providerThreadId: turnInput.providerThread.id, }), ), interruptTurn: Effect.fn("CursorAdapterV2.interruptTurn")( - function* (turnInput: ProviderAdapterV2InterruptInput) { + function* (turnInput: ProviderAdapter.ProviderAdapterV2InterruptInput) { const context = yield* Ref.get(activeTurn); if (context?.providerTurnId !== turnInput.providerTurnId) { - return yield* new ProviderAdapterProtocolError({ - driver: CURSOR_PROVIDER, + return yield* new ProviderAdapter.ProviderAdapterProtocolError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, detail: `Cursor provider turn ${turnInput.providerTurnId} is not active.`, }); } @@ -2481,8 +2454,8 @@ export function makeCursorAdapterV2( effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterInterruptError({ - driver: CURSOR_PROVIDER, + new ProviderAdapter.ProviderAdapterInterruptError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, providerThreadId: turnInput.providerThread.id, providerTurnId: turnInput.providerTurnId, cause, @@ -2492,11 +2465,11 @@ export function makeCursorAdapterV2( ), respondToRuntimeRequest: (requestInput) => Effect.fail( - new ProviderAdapterRuntimeRequestResponseError({ - driver: CURSOR_PROVIDER, + new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, requestId: requestInput.requestId, - cause: new ProviderAdapterProtocolError({ - driver: CURSOR_PROVIDER, + cause: new ProviderAdapter.ProviderAdapterProtocolError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, detail: "Cursor Agent SDK does not expose interactive approval requests.", }), }), @@ -2525,7 +2498,7 @@ export function makeCursorAdapterV2( createdBy: message.type === "user" ? "user" : "agent", creationSource: "provider", id: idAllocator.derive.messageFromProviderItem({ - driver: CURSOR_PROVIDER, + driver: CursorAgentSdk.CURSOR_PROVIDER, nativeItemId: message.uuid, }), threadId, @@ -2561,8 +2534,8 @@ export function makeCursorAdapterV2( effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterReadThreadSnapshotError({ - driver: CURSOR_PROVIDER, + new ProviderAdapter.ProviderAdapterReadThreadSnapshotError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, providerThreadId: snapshotInput.providerThread.id, cause, }), @@ -2571,8 +2544,8 @@ export function makeCursorAdapterV2( ), rollbackThread: (rollbackInput) => Effect.fail( - new ProviderAdapterRollbackThreadError({ - driver: CURSOR_PROVIDER, + new ProviderAdapter.ProviderAdapterRollbackThreadError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, providerThreadId: rollbackInput.providerThread.id, checkpointId: rollbackInput.target.checkpointId, cause: "Cursor Agent SDK does not expose conversation rollback.", @@ -2580,8 +2553,8 @@ export function makeCursorAdapterV2( ), forkThread: (forkInput) => Effect.fail( - new ProviderAdapterForkThreadError({ - driver: CURSOR_PROVIDER, + new ProviderAdapter.ProviderAdapterForkThreadError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, providerThreadId: forkInput.sourceProviderThread.id, cause: "Cursor Agent SDK does not expose native agent forks.", }), @@ -2593,8 +2566,8 @@ export function makeCursorAdapterV2( effect.pipe( Effect.mapError( (cause) => - new ProviderAdapterOpenSessionError({ - driver: CURSOR_PROVIDER, + new ProviderAdapter.ProviderAdapterOpenSessionError({ + driver: CursorAgentSdk.CURSOR_PROVIDER, providerSessionId: input.providerSessionId, cause, }), @@ -2605,11 +2578,11 @@ export function makeCursorAdapterV2( } export type CursorAdapterV2DriverEnv = - | CursorAgentSdkRunner + | CursorAgentSdk.CursorAgentSdkRunner | FileSystem.FileSystem | Path.Path - | IdAllocatorV2 - | ServerConfig; + | IdAllocator.IdAllocatorV2 + | ServerConfig.ServerConfig; export const CursorAdapterV2Driver: ProviderAdapterDriver< CursorSettings, @@ -2623,9 +2596,9 @@ export const CursorAdapterV2Driver: ProviderAdapterDriver< const hostEnvironment = yield* HostProcessEnvironment; const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; - const idAllocator = yield* IdAllocatorV2; - const runner = yield* CursorAgentSdkRunner; - const serverConfig = yield* ServerConfig; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const runner = yield* CursorAgentSdk.CursorAgentSdkRunner; + const serverConfig = yield* ServerConfig.ServerConfig; return makeCursorAdapterV2({ instanceId: input.instanceId, settings: { @@ -2656,18 +2629,22 @@ export const CursorAdapterV2Driver: ProviderAdapterDriver< }; const layer: Layer.Layer< - ProviderAdapterV2, + ProviderAdapter.ProviderAdapterV2, never, - CursorAgentSdkRunner | FileSystem.FileSystem | Path.Path | IdAllocatorV2 | ServerConfig + | CursorAgentSdk.CursorAgentSdkRunner + | FileSystem.FileSystem + | Path.Path + | IdAllocator.IdAllocatorV2 + | ServerConfig.ServerConfig > = Layer.effect( - ProviderAdapterV2, + ProviderAdapter.ProviderAdapterV2, Effect.gen(function* () { const hostEnvironment = yield* HostProcessEnvironment; const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; - const idAllocator = yield* IdAllocatorV2; - const runner = yield* CursorAgentSdkRunner; - const serverConfig = yield* ServerConfig; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const runner = yield* CursorAgentSdk.CursorAgentSdkRunner; + const serverConfig = yield* ServerConfig.ServerConfig; return makeCursorAdapterV2({ instanceId: CURSOR_DEFAULT_INSTANCE_ID, settings: DEFAULT_CURSOR_SETTINGS, diff --git a/apps/server/src/orchestration-v2/Adapters/CursorAgentSdk.test.ts b/apps/server/src/orchestration-v2/Adapters/CursorAgentSdk.test.ts index c546d8e5ece3..31f50972f49d 100644 --- a/apps/server/src/orchestration-v2/Adapters/CursorAgentSdk.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/CursorAgentSdk.test.ts @@ -5,12 +5,8 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import { vi } from "vite-plus/test"; -import { ProviderEventLoggers } from "../../provider/Layers/ProviderEventLoggers.ts"; -import { - CursorAgentSdkRunner, - CursorAgentSdkRunnerError, - cursorAgentSdkRunnerLiveLayer, -} from "./CursorAgentSdk.ts"; +import * as ProviderEventLoggers from "../../provider/Layers/ProviderEventLoggers.ts"; +import * as CursorAgentSdk from "./CursorAgentSdk.ts"; const cursorSdkMock = vi.hoisted(() => { const closeExecutionOrder: Array = []; @@ -108,9 +104,9 @@ vi.mock("../../provider/cursorSdk.ts", () => ({ }, })); -const testLayer = cursorAgentSdkRunnerLiveLayer.pipe( +const testLayer = CursorAgentSdk.cursorAgentSdkRunnerLiveLayer.pipe( Layer.provide( - Layer.succeed(ProviderEventLoggers, { + Layer.succeed(ProviderEventLoggers.ProviderEventLoggers, { native: { filePath: "cursor-agent-sdk-test.log", write: (event: unknown) => @@ -137,7 +133,7 @@ describe("CursorAgentSdkRunner", () => { cursorSdkMock.runOperations.length = 0; cursorSdkMock.setStaleRunActive(false); - const runner = yield* CursorAgentSdkRunner; + const runner = yield* CursorAgentSdk.CursorAgentSdkRunner; const session = yield* runner.open({ operation: "resume", agentId: "agent-cursor-agent-sdk-test", @@ -161,7 +157,7 @@ describe("CursorAgentSdkRunner", () => { cursorSdkMock.cancelRun.mockClear(); cursorSdkMock.setStaleRunActive(true); - const runner = yield* CursorAgentSdkRunner; + const runner = yield* CursorAgentSdk.CursorAgentSdkRunner; const session = yield* runner.open({ operation: "resume", agentId: "agent-cursor-agent-sdk-test", @@ -201,7 +197,7 @@ describe("CursorAgentSdkRunner", () => { cursorSdkMock.closeExecutionOrder.length = 0; cursorSdkMock.setAgentCloseFailure(undefined); - const runner = yield* CursorAgentSdkRunner; + const runner = yield* CursorAgentSdk.CursorAgentSdkRunner; const session = yield* runner.open({ operation: "create", options: { @@ -235,7 +231,7 @@ describe("CursorAgentSdkRunner", () => { const closeFailure = new Error("agent close failed"); cursorSdkMock.setAgentCloseFailure(closeFailure); - const runner = yield* CursorAgentSdkRunner; + const runner = yield* CursorAgentSdk.CursorAgentSdkRunner; const session = yield* runner.open({ operation: "create", options: { @@ -254,7 +250,7 @@ describe("CursorAgentSdkRunner", () => { cursorSdkMock.closeExecutionOrder.length = 0; const error = yield* Effect.flip(session.close); - assert.instanceOf(error, CursorAgentSdkRunnerError); + assert.instanceOf(error, CursorAgentSdk.CursorAgentSdkRunnerError); assert.equal(error.method, "agent.close"); assert.strictEqual(error.cause, closeFailure); assert.deepStrictEqual(cursorSdkMock.closeExecutionOrder, [ @@ -272,7 +268,7 @@ describe("CursorAgentSdkRunner", () => { cursorSdkMock.runWait.mockClear(); cursorSdkMock.send.mockClear(); - const runner = yield* CursorAgentSdkRunner; + const runner = yield* CursorAgentSdk.CursorAgentSdkRunner; const session = yield* runner.open({ operation: "create", options: { @@ -297,7 +293,7 @@ describe("CursorAgentSdkRunner", () => { const error = yield* Effect.flip(run.wait); - assert.instanceOf(error, CursorAgentSdkRunnerError); + assert.instanceOf(error, CursorAgentSdk.CursorAgentSdkRunnerError); assert.equal(error.method, "run.wait"); assert.strictEqual(error.cause, callbackFailure); assert.equal(cursorSdkMock.create.mock.calls.length, 1); diff --git a/apps/server/src/orchestration-v2/Adapters/CursorAgentSdk.ts b/apps/server/src/orchestration-v2/Adapters/CursorAgentSdk.ts index 869e95bad0a0..b94fa260caa0 100644 --- a/apps/server/src/orchestration-v2/Adapters/CursorAgentSdk.ts +++ b/apps/server/src/orchestration-v2/Adapters/CursorAgentSdk.ts @@ -18,7 +18,7 @@ import * as Schema from "effect/Schema"; import { Agent, createAgentPlatform } from "../../provider/cursorSdk.ts"; import type { EventNdjsonLogger } from "../../provider/Layers/EventNdjsonLogger.ts"; -import { ProviderEventLoggers } from "../../provider/Layers/ProviderEventLoggers.ts"; +import * as ProviderEventLoggers from "../../provider/Layers/ProviderEventLoggers.ts"; export const CURSOR_AGENT_SDK_PROTOCOL = "cursor-agent-sdk.local" as const; export const CURSOR_PROVIDER = ProviderDriverKind.make("cursor"); @@ -596,11 +596,11 @@ export function makeCursorAgentSdkRunner( export const cursorAgentSdkRunnerLiveLayer: Layer.Layer< CursorAgentSdkRunner, never, - ProviderEventLoggers + ProviderEventLoggers.ProviderEventLoggers > = Layer.effect( CursorAgentSdkRunner, Effect.gen(function* () { - const { native: nativeEventLogger } = yield* ProviderEventLoggers; + const { native: nativeEventLogger } = yield* ProviderEventLoggers.ProviderEventLoggers; return makeCursorAgentSdkRunner((input) => makeCursorAgentSdkProtocolLogger({ nativeEventLogger, diff --git a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts index 95c9bc3958d4..60be8b82f067 100644 --- a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts @@ -22,17 +22,14 @@ import * as Schema from "effect/Schema"; import { ChildProcessSpawner } from "effect/unstable/process"; import type * as EffectAcpSchema from "effect-acp/compat"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import * as ProjectStore from "../ProjectStore.ts"; import { buildInitialGrokProviderSnapshot } from "../../provider/Layers/GrokProvider.ts"; import type { ProviderInstance } from "../../provider/ProviderDriver.ts"; -import { ProviderInstanceRegistry } from "../../provider/Services/ProviderInstanceRegistry.ts"; -import { layer as idAllocatorLayer, IdAllocatorV2 } from "../IdAllocator.ts"; +import * as ProviderInstanceRegistry from "../../provider/Services/ProviderInstanceRegistry.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { ProviderAdapterV2RuntimePolicy } from "../ProviderAdapter.ts"; -import { - layerFromProjectStore as runtimePolicyLayerFromProjectStore, - RuntimePolicyV2, -} from "../RuntimePolicy.ts"; +import * as RuntimePolicy from "../RuntimePolicy.ts"; import { acpPermissionDisposition } from "../../provider/acp/AcpClientPolicy.ts"; import { AcpProviderCapabilitiesV2, @@ -317,7 +314,7 @@ describe("Grok launch permission mode", () => { const serverConfigLayer = ServerConfig.layerTest(process.cwd(), { prefix: "t3-grok-v2-launch-", }).pipe(Layer.provide(NodeServices.layer)); - const testLayer = Layer.mergeAll(NodeServices.layer, idAllocatorLayer, serverConfigLayer); + const testLayer = Layer.mergeAll(NodeServices.layer, IdAllocator.layer, serverConfigLayer); // Opens a session through the adapter's own Grok runtime factory and returns // the argv it tried to launch. The spawn fails after recording, so no @@ -344,8 +341,8 @@ describe("Grok launch permission mode", () => { childProcessSpawner, crypto: yield* Crypto.Crypto, fileSystem: yield* FileSystem.FileSystem, - idAllocator: yield* IdAllocatorV2, - serverConfig: yield* ServerConfig, + idAllocator: yield* IdAllocator.IdAllocatorV2, + serverConfig: yield* ServerConfig.ServerConfig, selfInvocation: yield* resolveSelfInvocation(), }); yield* adapter @@ -395,7 +392,7 @@ describe("Grok launch permission mode", () => { const threadId = ThreadId.make("grok-launch-test"); const modelSelection = { instanceId, model: "grok-build" } as const; const resolved = yield* Effect.gen(function* () { - const runtimePolicy = yield* RuntimePolicyV2; + const runtimePolicy = yield* RuntimePolicy.RuntimePolicyV2; return yield* runtimePolicy.resolve({ thread: { createdBy: "user", @@ -424,14 +421,14 @@ describe("Grok launch permission mode", () => { }); }).pipe( Effect.provide( - runtimePolicyLayerFromProjectStore.pipe( + RuntimePolicy.layerFromProjectStore.pipe( Layer.provide( Layer.mock(ProjectStore.ProjectStoreV2)({ get: () => Effect.die("the thread has a worktree"), }), ), Layer.provide( - Layer.mock(ProviderInstanceRegistry)({ + Layer.mock(ProviderInstanceRegistry.ProviderInstanceRegistry)({ getInstance: () => Effect.succeed({ snapshot: { getSnapshot: Effect.succeed(snapshot) }, diff --git a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.testkit.ts index eb08c0697fd5..e997d21d1a4c 100644 --- a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.testkit.ts @@ -10,15 +10,15 @@ import { ChildProcessSpawner } from "effect/unstable/process"; import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; import { resolveSelfInvocation } from "@t3tools/shared/nodeRuntime"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import { GROK_ACP_CANCEL_META, GROK_ACP_INITIALIZE_META, } from "../../provider/acp/GrokAcpSupport.ts"; import { makeXAiPromptCompletionRuntime } from "../../provider/acp/XAiAcpExtension.ts"; -import { layer as idAllocatorLayer, IdAllocatorV2 } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import * as ProviderContinuationRequests from "../ProviderContinuationRequests.ts"; -import { makeLayerEffect as makeProviderAdapterRegistryLayerEffect } from "../ProviderAdapterRegistry.ts"; +import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; import type { ProviderReplayGate } from "../testkit/ProviderReplayGate.testkit.ts"; import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts"; import { makeReplayServerConfig } from "../testkit/ProviderReplayHarness.ts"; @@ -38,19 +38,19 @@ function makeGrokProviderAdapterRegistryReplayLayer( options: { readonly replayGate?: ProviderReplayGate } = {}, ) { const serverConfigLayer = Layer.effect( - ServerConfig, + ServerConfig.ServerConfig, makeReplayServerConfig(`grok-${transcript.scenario}`).pipe(Effect.orDie), ).pipe(Layer.provide(NodeServices.layer)); - return makeProviderAdapterRegistryLayerEffect( + return ProviderAdapterRegistry.makeLayerEffect( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const crypto = yield* Crypto.Crypto; const hostPlatform = yield* HostProcessPlatform; - const idAllocator = yield* IdAllocatorV2; - const serverConfig = yield* ServerConfig; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const serverConfig = yield* ServerConfig.ServerConfig; // Same queue the continuation worker drains when the fixture runs it. const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests; const replayGate = options.replayGate; @@ -102,7 +102,7 @@ function makeGrokProviderAdapterRegistryReplayLayer( return [adapter]; }), ).pipe( - Layer.provide(Layer.mergeAll(serverConfigLayer, NodeServices.layer, idAllocatorLayer)), + Layer.provide(Layer.mergeAll(serverConfigLayer, NodeServices.layer, IdAllocator.layer)), // Held inbound lines must not outlive the scenario and wedge teardown. Layer.merge( Layer.effectDiscard( diff --git a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.ts index a02e9ef30a65..0357c4b95ab8 100644 --- a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.ts @@ -19,7 +19,7 @@ import type * as Scope from "effect/Scope"; import { ChildProcessSpawner } from "effect/unstable/process"; import * as EffectAcpErrors from "effect-acp/errors"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import { makeAcpNativeLoggerFactory } from "../../provider/acp/AcpNativeLogging.ts"; import { applyGrokAcpModelSelection, @@ -52,10 +52,10 @@ import { import { mergeProviderInstanceEnvironment } from "../../provider/ProviderInstanceEnvironment.ts"; import { acpPermissionDisposition } from "../../provider/acp/AcpClientPolicy.ts"; import * as AcpSessionRuntime from "../../provider/acp/AcpSessionRuntime.ts"; -import { ProviderEventLoggers } from "../../provider/Layers/ProviderEventLoggers.ts"; -import { IdAllocatorV2 } from "../IdAllocator.ts"; -import { ProviderContinuationRequests } from "../ProviderContinuationRequests.ts"; -import { ProviderAdapterV2, type ProviderAdapterV2RuntimePolicy } from "../ProviderAdapter.ts"; +import * as ProviderEventLoggers from "../../provider/Layers/ProviderEventLoggers.ts"; +import * as IdAllocator from "../IdAllocator.ts"; +import * as ProviderContinuationRequests from "../ProviderContinuationRequests.ts"; +import * as ProviderAdapter from "../ProviderAdapter.ts"; import { ProviderAdapterDriverCreateError, type ProviderAdapterDriver, @@ -112,8 +112,8 @@ export interface GrokAdapterV2Options { readonly crypto: Crypto.Crypto; readonly selfInvocation: SelfInvocation; readonly fileSystem: FileSystem.FileSystem; - readonly idAllocator: IdAllocatorV2["Service"]; - readonly serverConfig: ServerConfig["Service"]; + readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; + readonly serverConfig: ServerConfig.ServerConfig["Service"]; readonly nativeLogging?: Parameters[0]["nativeLogging"]; readonly continuationRequests?: Parameters[0]["continuationRequests"]; readonly testHooks?: Parameters[0]["testHooks"]; @@ -217,7 +217,9 @@ const registerGrokAskUserQuestionExtensions = ({ * overrides launch it asking, so every mutating prompt reaches T3's policy * check instead of being bypassed by always-approve or Grok's auto classifier. */ -export function grokLaunchRuntimeMode(runtimePolicy: ProviderAdapterV2RuntimePolicy): RuntimeMode { +export function grokLaunchRuntimeMode( + runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy, +): RuntimeMode { return runtimePolicy.approvalPolicy === undefined && runtimePolicy.sandboxPolicy === undefined ? runtimePolicy.runtimeMode : "approval-required"; @@ -339,10 +341,10 @@ export type GrokAdapterV2DriverEnv = | ChildProcessSpawner.ChildProcessSpawner | Crypto.Crypto | FileSystem.FileSystem - | IdAllocatorV2 + | IdAllocator.IdAllocatorV2 | Path.Path - | ProviderEventLoggers - | ServerConfig; + | ProviderEventLoggers.ProviderEventLoggers + | ServerConfig.ServerConfig; export const GrokAdapterV2Driver: ProviderAdapterDriver = { driverKind: GROK_DRIVER_KIND, @@ -356,10 +358,10 @@ export const GrokAdapterV2Driver: ProviderAdapterDriver = Layer.effect( - ProviderAdapterV2, + ProviderAdapter.ProviderAdapterV2, Effect.gen(function* () { const hostEnvironment = yield* HostProcessEnvironment; const hostPlatform = yield* HostProcessPlatform; @@ -415,10 +417,10 @@ const layer: Layer.Layer< const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; const crypto = yield* Crypto.Crypto; const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; - const providerEventLoggers = yield* ProviderEventLoggers; - const serverConfig = yield* ServerConfig; - const continuationRequests = yield* ProviderContinuationRequests; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const providerEventLoggers = yield* ProviderEventLoggers.ProviderEventLoggers; + const serverConfig = yield* ServerConfig.ServerConfig; + const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests; const makeNativeLogger = yield* makeAcpNativeLoggerFactory(); return makeGrokAdapterV2({ instanceId: GROK_DEFAULT_INSTANCE_ID, diff --git a/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.test.ts index fa32da8c4637..3a3f9a2af6cb 100644 --- a/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.test.ts @@ -31,10 +31,10 @@ import * as Scope from "effect/Scope"; import * as Stream from "effect/Stream"; import * as TestClock from "effect/testing/TestClock"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import type { EventNdjsonLogger } from "../../provider/Layers/EventNdjsonLogger.ts"; import type { OpenCodeRuntimeShape } from "../../provider/opencodeRuntime.ts"; -import { IdAllocatorV2, layer as idAllocatorLayer } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { advanceOpenCodePromptAdmission, @@ -147,7 +147,7 @@ const makeOpenCodeRuntimeHarness = Effect.fn("makeOpenCodeRuntimeHarness")(funct nativeSessionId: string, client: object, ) { - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const instanceId = ProviderInstanceId.make(`opencode-${suffix}`); const threadId = ThreadId.make(`thread-opencode-${suffix}`); const modelSelection = { @@ -168,7 +168,7 @@ const makeOpenCodeRuntimeHarness = Effect.fn("makeOpenCodeRuntimeHarness")(funct serverConfig: { cwd: "/workspace", attachmentsDir: "/tmp/attachments", - } as ServerConfig["Service"], + } as ServerConfig.ServerConfig["Service"], }); const runtime = yield* adapter.openSession({ threadId, @@ -374,7 +374,7 @@ describe("OpenCodeAdapterV2", () => { hasSubagents: false, }, ); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); } @@ -452,7 +452,7 @@ describe("OpenCodeAdapterV2", () => { deliver = true; // Failed delivery leaves the request available for an explicit retry. yield* harness.runtime.respondToRuntimeRequest(response); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); } @@ -491,7 +491,7 @@ describe("OpenCodeAdapterV2", () => { "children:child", "stream.close", ]); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); for (const failure of ["enumeration", "abort", "not-found", "timeout"] as const) { @@ -544,7 +544,7 @@ describe("OpenCodeAdapterV2", () => { const result = yield* Fiber.join(stop); assert.equal(Exit.isSuccess(result), failure === "not-found"); if (failure === "timeout") assert.isTrue(childSignal?.aborted); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); } @@ -709,7 +709,7 @@ describe("OpenCodeAdapterV2", () => { const assistant = items.filter((item) => item.type === "assistant_message"); assert.equal(assistant.at(-1)?.text, "Tool results received"); assert.equal(assistant.at(-1)?.status, "completed"); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); // Event order from a live OpenCode 1.18.32 run of a `task` call with @@ -804,7 +804,7 @@ describe("OpenCodeAdapterV2", () => { yield* push(status(child, "idle")); yield* push({ type: "session.idle", properties: { sessionID: child } }); assert.isFalse(yield* hasPendingBackgroundWork, "an idle child must not pin idle release"); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); it.effect("titles OpenCode reads and searches from their input", () => @@ -881,7 +881,7 @@ describe("OpenCodeAdapterV2", () => { assert.deepEqual(webSearch?.type === "web_search" ? webSearch.patterns : null, [ "OpenCode documentation", ]); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); it.effect("admits a native command on its user receipt before generation completes", () => @@ -944,7 +944,7 @@ describe("OpenCodeAdapterV2", () => { yield* Fiber.join(start); assert.equal(promptCalls, 0); release.resolve(); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); it.effect("sends unadvertised slash commands as ordinary prompts", () => @@ -971,7 +971,7 @@ describe("OpenCodeAdapterV2", () => { }); yield* harness.startTurn("/unknown words"); assert.deepEqual(prompts, [[{ type: "text", text: "/unknown words" }]]); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); it.effect("compacts with the native summarize API and emits a completed compaction", () => @@ -1026,7 +1026,7 @@ describe("OpenCodeAdapterV2", () => { assert.isTrue( events.some((event) => event.type === "turn.terminal" && event.status === "completed"), ); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); it.effect("does not restore messages beyond OpenCode's persisted revert boundary", () => @@ -1111,12 +1111,12 @@ describe("OpenCodeAdapterV2", () => { ["user-kept", "assistant-kept"], ); assert.equal(snapshot.providerThread.nativeConversationHeadRef?.nativeId, "user-kept"); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); it.effect("keeps a newly admitted prompt alive across stale idle and delayed busy evidence", () => Effect.gen(function* () { - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const nativeEvents = asyncEventStream(); const prompt = promiseGate(); const promptStarted = promiseGate(); @@ -1196,7 +1196,7 @@ describe("OpenCodeAdapterV2", () => { serverConfig: { cwd: "/workspace", attachmentsDir: "/tmp/attachments", - } as ServerConfig["Service"], + } as ServerConfig.ServerConfig["Service"], }); const threadId = ThreadId.make("thread-opencode-admission-race"); const providerSessionId = ProviderSessionId.make("session-opencode-admission-race"); @@ -1429,12 +1429,12 @@ describe("OpenCodeAdapterV2", () => { ); const interrupted = yield* runtime.readThreadSnapshot({ providerThread }); assert.equal(interrupted.providerTurns.at(-1)?.status, "interrupted"); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); it.effect("interrupts an initial prompt while its SDK request is pending", () => Effect.gen(function* () { - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const nativeEvents = asyncEventStream(); const promptStarted = promiseGate(); const abortCalled = promiseGate(); @@ -1491,7 +1491,7 @@ describe("OpenCodeAdapterV2", () => { serverConfig: { cwd: "/workspace", attachmentsDir: "/tmp/attachments", - } as ServerConfig["Service"], + } as ServerConfig.ServerConfig["Service"], }); const threadId = ThreadId.make("thread-opencode-initial-stop"); const providerSessionId = ProviderSessionId.make("session-opencode-initial-stop"); @@ -1595,7 +1595,7 @@ describe("OpenCodeAdapterV2", () => { (event.type === "provider_session.updated" && event.providerSession.status === "error"), ), ); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); it.effect("fails an active turn when the OpenCode event stream ends cleanly", () => @@ -1638,7 +1638,7 @@ describe("OpenCodeAdapterV2", () => { assert.equal(terminal?.failure?.class, "transport_error"); assert.equal(terminal?.threadDisposition, "broken"); assert.equal((yield* Effect.exit(harness.startTurn()))._tag, "Failure"); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); it.effect("fails compaction when its response races stream termination", () => @@ -1699,7 +1699,7 @@ describe("OpenCodeAdapterV2", () => { event.turnItem.status === "completed", ), ); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); it.effect("does not register a turn after the OpenCode event stream ends", () => @@ -1757,7 +1757,7 @@ describe("OpenCodeAdapterV2", () => { assert.isTrue(Exit.isFailure(yield* Fiber.join(start))); assert.equal(promptCalls, 0); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); it("holds stale idle through prompt admission until the new user message is observed", () => { @@ -1935,7 +1935,7 @@ describe("OpenCodeAdapterV2", () => { assert.lengthOf(terminals, 1); assert.equal(terminals[0]?.status, "completed"); assert.isNull(terminals[0]?.failure ?? null); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); it.effect("does not let a queued status retry adopt a newer steer generation", () => @@ -2038,7 +2038,7 @@ describe("OpenCodeAdapterV2", () => { }); assert.equal(statusCallCount, 1); assert.equal(afterRetry.providerTurns.at(-1)?.status, "running"); - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); it.effect("ignores a delayed status reply after steering starts a newer admission", () => @@ -2081,7 +2081,7 @@ describe("OpenCodeAdapterV2", () => { it.effect("logs bounded structural protocol diagnostics without native payload values", () => Effect.gen(function* () { - const idAllocator = yield* IdAllocatorV2; + const idAllocator = yield* IdAllocator.IdAllocatorV2; const records: Array = []; const nativeEventLogger: EventNdjsonLogger = { filePath: "/tmp/provider-native.ndjson", @@ -2109,13 +2109,13 @@ describe("OpenCodeAdapterV2", () => { assert.include(serialized, '"protocol":"opencode-sdk.sse"'); assert.include(serialized, '"method":"session.prompt"'); assert.include(serialized, '"fieldCount":2'); - }).pipe(Effect.provide(idAllocatorLayer)), + }).pipe(Effect.provide(IdAllocator.layer)), ); it.effect("adopts the handed-over provider thread identity on session create", () => Effect.gen(function* () { - const idAllocator = yield* IdAllocatorV2; - const serverConfig = yield* ServerConfig; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const serverConfig = yield* ServerConfig.ServerConfig; let createCount = 0; const createInputs: Array = []; const fakeClient = { @@ -2222,10 +2222,10 @@ describe("OpenCodeAdapterV2", () => { Effect.scoped, Effect.provide( Layer.mergeAll( - idAllocatorLayer, - ServerConfig.layerTest(process.cwd(), { prefix: "t3-opencode-v2-adapter-" }).pipe( - Layer.provide(NodeServices.layer), - ), + IdAllocator.layer, + ServerConfig.layerTest(process.cwd(), { + prefix: "t3-opencode-v2-adapter-", + }).pipe(Layer.provide(NodeServices.layer)), ), ), ), @@ -2423,5 +2423,5 @@ it.effect.each([false, true])( assert.equal(result.messages.length, 0); assert.deepEqual(calls, ["fork", "permissions"]); } - }).pipe(Effect.provide(idAllocatorLayer), Effect.scoped), + }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), ); diff --git a/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.testkit.ts index 74be69433fbe..4112394422a5 100644 --- a/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.testkit.ts @@ -6,19 +6,12 @@ import * as Duration from "effect/Duration"; import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; -import { ServerConfig } from "../../config.ts"; -import { - OpenCodeRuntime, - OpenCodeRuntimeError, - type OpenCodeRuntimeShape, -} from "../../provider/opencodeRuntime.ts"; -import { - NoOpProviderEventLoggers, - ProviderEventLoggers, -} from "../../provider/Layers/ProviderEventLoggers.ts"; -import { layer as idAllocatorLayer } from "../IdAllocator.ts"; +import * as ServerConfig from "../../config.ts"; +import * as OpenCodeRuntime from "../../provider/opencodeRuntime.ts"; +import * as ProviderEventLoggers from "../../provider/Layers/ProviderEventLoggers.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { ProviderAdapterDriverCreateError } from "../ProviderAdapterDriver.ts"; -import { makeDriverLayer as makeProviderAdapterRegistryDriverLayer } from "../ProviderAdapterRegistry.ts"; +import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; import { makeReplayServerConfig, type OrchestratorV2ProviderReplayHarness, @@ -341,7 +334,7 @@ function makeReplayClient(controller: OpenCodeReplayController): OpencodeClient function makeOpenCodeReplayRuntimeLayer(transcript: OpenCodeSdkReplayTranscript) { return Layer.effect( - OpenCodeRuntime, + OpenCodeRuntime.OpenCodeRuntime, Effect.gen(function* () { const controller = new OpenCodeReplayController(transcript); yield* Effect.addFinalizer(() => @@ -350,10 +343,10 @@ function makeOpenCodeReplayRuntimeLayer(transcript: OpenCodeSdkReplayTranscript) }), ); const client = makeReplayClient(controller); - return OpenCodeRuntime.of({ + return OpenCodeRuntime.OpenCodeRuntime.of({ startOpenCodeServerProcess: () => Effect.fail( - new OpenCodeRuntimeError({ + new OpenCodeRuntime.OpenCodeRuntimeError({ operation: "startOpenCodeServerProcess", detail: "OpenCode replay uses an external in-memory SDK boundary.", }), @@ -367,7 +360,7 @@ function makeOpenCodeReplayRuntimeLayer(transcript: OpenCodeSdkReplayTranscript) }), runOpenCodeCommand: () => Effect.fail( - new OpenCodeRuntimeError({ + new OpenCodeRuntime.OpenCodeRuntimeError({ operation: "runOpenCodeCommand", detail: "OpenCode replay does not execute commands.", }), @@ -375,43 +368,43 @@ function makeOpenCodeReplayRuntimeLayer(transcript: OpenCodeSdkReplayTranscript) createOpenCodeSdkClient: () => client, loadOpenCodeInventory: () => Effect.fail( - new OpenCodeRuntimeError({ + new OpenCodeRuntime.OpenCodeRuntimeError({ operation: "loadOpenCodeInventory", detail: "OpenCode replay does not load inventory.", }), ), loadInventoryFromCli: () => Effect.fail( - new OpenCodeRuntimeError({ + new OpenCodeRuntime.OpenCodeRuntimeError({ operation: "loadInventoryFromCli", detail: "OpenCode replay does not load inventory.", }), ), loadOpenCodeSkills: () => Effect.fail( - new OpenCodeRuntimeError({ + new OpenCodeRuntime.OpenCodeRuntimeError({ operation: "loadOpenCodeSkills", detail: "OpenCode replay does not load skills.", }), ), loadSkillsFromCli: () => Effect.fail( - new OpenCodeRuntimeError({ + new OpenCodeRuntime.OpenCodeRuntimeError({ operation: "loadSkillsFromCli", detail: "OpenCode replay does not load skills.", }), ), - } satisfies OpenCodeRuntimeShape); + } satisfies OpenCodeRuntime.OpenCodeRuntimeShape); }), ); } function makeOpenCodeProviderAdapterRegistryReplayLayer(transcript: OpenCodeSdkReplayTranscript) { const serverConfigLayer = Layer.effect( - ServerConfig, + ServerConfig.ServerConfig, makeReplayServerConfig(transcript.scenario).pipe(Effect.orDie), ).pipe(Layer.provide(NodeServices.layer)); - return makeProviderAdapterRegistryDriverLayer({ + return ProviderAdapterRegistry.makeDriverLayer({ drivers: [OpenCodeAdapterV2Driver], configMap: { [OPENCODE_DEFAULT_INSTANCE_ID]: { @@ -425,8 +418,11 @@ function makeOpenCodeProviderAdapterRegistryReplayLayer(transcript: OpenCodeSdkR makeOpenCodeReplayRuntimeLayer(transcript), serverConfigLayer, NodeServices.layer, - idAllocatorLayer, - Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers), + IdAllocator.layer, + Layer.succeed( + ProviderEventLoggers.ProviderEventLoggers, + ProviderEventLoggers.NoOpProviderEventLoggers, + ), ), ), ); diff --git a/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.test.ts index b82dad9078ed..faf1ba01c3c2 100644 --- a/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.test.ts @@ -31,9 +31,9 @@ import * as Stream from "effect/Stream"; import * as TestClock from "effect/testing/TestClock"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import * as McpProviderSession from "../../mcp/McpProviderSession.ts"; -import { IdAllocatorV2, layer as idAllocatorLayer } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { ProviderAdapterV2RuntimePolicy, type ProviderAdapterV2Event, @@ -47,7 +47,7 @@ const serverConfigLayer = ServerConfig.layerTest(process.cwd(), { prefix: "t3-pi-v2-adapter-", }).pipe(Layer.provide(NodeServices.layer)); -const testLayer = Layer.mergeAll(NodeServices.layer, idAllocatorLayer, serverConfigLayer); +const testLayer = Layer.mergeAll(NodeServices.layer, IdAllocator.layer, serverConfigLayer); const decodeJsonLine = Schema.decodeSync(Schema.fromJsonString(Schema.Unknown)); const encodeJsonLine = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown)); @@ -311,8 +311,8 @@ const makeFakePi: Effect.Effect = Effect.gen(function* () { }); const makeAdapter = Effect.fnUntraced(function* (fake: FakePi, launchArgs = "", forkFake?: FakePi) { - const idAllocator = yield* IdAllocatorV2; - const serverConfig = yield* ServerConfig; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const serverConfig = yield* ServerConfig.ServerConfig; const fileSystem = yield* FileSystem.FileSystem; return makePiAdapterV2({ instanceId: PI_INSTANCE_ID, diff --git a/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.testkit.ts index ca2c721533c2..f8e99469b388 100644 --- a/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.testkit.ts @@ -30,10 +30,10 @@ import * as Sink from "effect/Sink"; import * as Stream from "effect/Stream"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import * as IdAllocator from "../IdAllocator.ts"; import { ProviderAdapterDriverCreateError } from "../ProviderAdapterDriver.ts"; -import { makeDriverLayer as makeProviderAdapterRegistryDriverLayer } from "../ProviderAdapterRegistry.ts"; +import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; import { makeReplayServerConfig, type OrchestratorV2ProviderReplayHarness, @@ -415,10 +415,10 @@ export function makePiProviderAdapterRegistryLayer(input: { readonly environment?: ProviderInstanceEnvironment; }) { const serverConfigLayer = Layer.effect( - ServerConfig, + ServerConfig.ServerConfig, makeReplayServerConfig(`pi-${input.scenario}`).pipe(Effect.orDie), ).pipe(Layer.provide(NodeServices.layer)); - return makeProviderAdapterRegistryDriverLayer({ + return ProviderAdapterRegistry.makeDriverLayer({ drivers: [PiAdapterV2Driver], configMap: { [PI_PROVIDER]: { diff --git a/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts index dc19647fa583..88489a1f9040 100644 --- a/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts @@ -60,7 +60,7 @@ import * as Stream from "effect/Stream"; import { ChildProcessSpawner } from "effect/unstable/process"; import { resolveAttachmentPath } from "../../attachmentStore.ts"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import * as McpProviderSession from "../../mcp/McpProviderSession.ts"; import { expandPiSkillReference, @@ -69,31 +69,8 @@ import { type PiCompactCommand, } from "../../provider/PiCommands.ts"; import { mergeProviderInstanceEnvironment } from "../../provider/ProviderInstanceEnvironment.ts"; -import { IdAllocatorV2 } from "../IdAllocator.ts"; -import { - ProviderAdapterEnsureThreadError, - ProviderAdapterEventStreamError, - ProviderAdapterForkThreadError, - ProviderAdapterInterruptError, - ProviderAdapterOpenSessionError, - ProviderAdapterProtocolError, - ProviderAdapterReadThreadSnapshotError, - ProviderAdapterResumeThreadError, - ProviderAdapterRollbackThreadError, - ProviderAdapterRuntimeRequestResponseError, - ProviderAdapterSteerRunError, - ProviderAdapterTurnStartError, - ProviderAdapterV2, - type ProviderAdapterV2Error, - type ProviderAdapterV2Event, - type ProviderAdapterV2EnsureThreadInput, - type ProviderAdapterV2OpenSessionInput, - type ProviderAdapterV2SessionRuntime, - type ProviderAdapterV2Shape, - type ProviderAdapterV2SteerInput, - type ProviderAdapterV2ThreadSnapshot, - type ProviderAdapterV2TurnInput, -} from "../ProviderAdapter.ts"; +import * as IdAllocator from "../IdAllocator.ts"; +import * as ProviderAdapter from "../ProviderAdapter.ts"; import { ProviderAdapterDriverCreateError, type ProviderAdapterDriver, @@ -248,8 +225,8 @@ export interface PiAdapterV2Options { readonly environment: NodeJS.ProcessEnv; readonly spawner: ChildProcessSpawner.ChildProcessSpawner["Service"]; readonly fileSystem: FileSystem.FileSystem; - readonly idAllocator: IdAllocatorV2["Service"]; - readonly serverConfig: ServerConfig["Service"]; + readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; + readonly serverConfig: ServerConfig.ServerConfig["Service"]; } /** Concatenate the `text` fields of a Pi content-block array. */ @@ -313,7 +290,7 @@ function compactionTitle(status: PiCompactionStatus): string { } interface ActivePiTurn { - readonly turnInput: ProviderAdapterV2TurnInput; + readonly turnInput: ProviderAdapter.ProviderAdapterV2TurnInput; readonly providerTurn: OrchestrationV2ProviderTurn; readonly startedAt: DateTime.Utc; readonly itemOrdinals: Map; @@ -390,23 +367,25 @@ interface PiThreadState { // ── adapter ─────────────────────────────────────────────────── -export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2Shape { +export function makePiAdapterV2( + options: PiAdapterV2Options, +): ProviderAdapter.ProviderAdapterV2Shape { const { idAllocator } = options; const protocolError = (detail: string, payload?: unknown) => - new ProviderAdapterProtocolError({ + new ProviderAdapter.ProviderAdapterProtocolError({ driver: PI_PROVIDER, detail, ...(payload === undefined ? {} : { payload }), }); - return ProviderAdapterV2.of({ + return ProviderAdapter.ProviderAdapterV2.of({ instanceId: options.instanceId, driver: PI_PROVIDER, getCapabilities: () => Effect.succeed(PiProviderCapabilitiesV2), planSelectionTransition: () => Effect.succeed(turnScopedSelectionTransition()), openSession: Effect.fn("PiAdapterV2.openSession")(function* ( - input: ProviderAdapterV2OpenSessionInput, + input: ProviderAdapter.ProviderAdapterV2OpenSessionInput, ) { const scope = yield* Effect.scope; const cwd = input.runtimePolicy.cwd ?? options.serverConfig.cwd; @@ -416,7 +395,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S Effect.provideService(FileSystem.FileSystem, options.fileSystem), Effect.mapError( (cause) => - new ProviderAdapterOpenSessionError({ + new ProviderAdapter.ProviderAdapterOpenSessionError({ driver: PI_PROVIDER, providerSessionId: input.providerSessionId, cause, @@ -449,7 +428,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, options.spawner), Effect.mapError( (cause) => - new ProviderAdapterOpenSessionError({ + new ProviderAdapter.ProviderAdapterOpenSessionError({ driver: PI_PROVIDER, providerSessionId: input.providerSessionId, cause, @@ -479,8 +458,8 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S lastError: null, }; const events = yield* Queue.unbounded< - ProviderAdapterV2Event, - ProviderAdapterV2Error | Cause.Done + ProviderAdapter.ProviderAdapterV2Event, + ProviderAdapter.ProviderAdapterV2Error | Cause.Done >(); const pendingPrompts = new Map(); const sessionApprovals = new Set(); @@ -544,7 +523,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S ? { type: "compact" } : { type: "compact", customInstructions: command.customInstructions }; - const emit = (event: ProviderAdapterV2Event) => + const emit = (event: ProviderAdapter.ProviderAdapterV2Event) => Queue.offer(events, event).pipe(Effect.asVoid); const updateProviderSession = ( @@ -1981,7 +1960,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S ); yield* Queue.fail( events, - new ProviderAdapterEventStreamError({ + new ProviderAdapter.ProviderAdapterEventStreamError({ driver: PI_PROVIDER, providerSessionId: input.providerSessionId, cause, @@ -2007,7 +1986,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S // ── session runtime ─────────────────────────────────── const registerThread = Effect.fnUntraced(function* ( - threadInput: ProviderAdapterV2EnsureThreadInput, + threadInput: ProviderAdapter.ProviderAdapterV2EnsureThreadInput, publish = true, ) { if (threadState !== null && threadState.activeTurn !== null) { @@ -2222,7 +2201,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S return { message, images }; }); - const runtime: ProviderAdapterV2SessionRuntime = { + const runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime = { instanceId: options.instanceId, driver: PI_PROVIDER, providerSessionId: input.providerSessionId, @@ -2244,7 +2223,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S registerThread(threadInput).pipe( Effect.mapError( (cause) => - new ProviderAdapterEnsureThreadError({ + new ProviderAdapter.ProviderAdapterEnsureThreadError({ driver: PI_PROVIDER, threadId: threadInput.threadId, cause, @@ -2261,7 +2240,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S }).pipe( Effect.mapError( (cause) => - new ProviderAdapterResumeThreadError({ + new ProviderAdapter.ProviderAdapterResumeThreadError({ driver: PI_PROVIDER, providerSessionId: input.providerSessionId, providerThreadId: threadInput.providerThread.id, @@ -2416,7 +2395,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S }).pipe( Effect.mapError( (cause) => - new ProviderAdapterTurnStartError({ + new ProviderAdapter.ProviderAdapterTurnStartError({ driver: PI_PROVIDER, threadId: turnInput.threadId, providerThreadId: turnInput.providerThread.id, @@ -2425,7 +2404,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S }), ), ), - steerTurn: (steerInput: ProviderAdapterV2SteerInput) => + steerTurn: (steerInput: ProviderAdapter.ProviderAdapterV2SteerInput) => Effect.gen(function* () { const turn = threadState?.activeTurn ?? null; if (turn === null || turn.providerTurn.id !== steerInput.providerTurnId) { @@ -2476,7 +2455,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S }).pipe( Effect.mapError( (cause) => - new ProviderAdapterSteerRunError({ + new ProviderAdapter.ProviderAdapterSteerRunError({ driver: PI_PROVIDER, providerThreadId: steerInput.providerThread.id, providerTurnId: steerInput.providerTurnId, @@ -2523,7 +2502,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S }).pipe( Effect.mapError( (cause) => - new ProviderAdapterInterruptError({ + new ProviderAdapter.ProviderAdapterInterruptError({ driver: PI_PROVIDER, providerThreadId: interruptInput.providerThread.id, providerTurnId: interruptInput.providerTurnId, @@ -2582,7 +2561,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S sessionEventPermit.withPermits(1), Effect.mapError( (cause) => - new ProviderAdapterRuntimeRequestResponseError({ + new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({ driver: PI_PROVIDER, requestId: requestInput.requestId, cause, @@ -2649,7 +2628,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S }).pipe( Effect.mapError( (cause) => - new ProviderAdapterReadThreadSnapshotError({ + new ProviderAdapter.ProviderAdapterReadThreadSnapshotError({ driver: PI_PROVIDER, providerThreadId: snapshotInput.providerThread.id, cause, @@ -2719,7 +2698,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S }).pipe( Effect.mapError( (cause) => - new ProviderAdapterRollbackThreadError({ + new ProviderAdapter.ProviderAdapterRollbackThreadError({ driver: PI_PROVIDER, providerThreadId: rollbackInput.providerThread.id, checkpointId: rollbackInput.target.checkpointId, @@ -2831,7 +2810,7 @@ export function makePiAdapterV2(options: PiAdapterV2Options): ProviderAdapterV2S }).pipe( Effect.mapError( (cause) => - new ProviderAdapterForkThreadError({ + new ProviderAdapter.ProviderAdapterForkThreadError({ driver: PI_PROVIDER, providerThreadId: forkInput.sourceProviderThread.id, cause, @@ -2904,7 +2883,7 @@ function piExtensionDisplayName(extensionPath: string | undefined): string { function piThreadSnapshot( providerThread: OrchestrationV2ProviderThread, -): ProviderAdapterV2ThreadSnapshot { +): ProviderAdapter.ProviderAdapterV2ThreadSnapshot { return { providerThread, providerTurns: [], messages: [], runtimeRequests: [] }; } @@ -2964,8 +2943,8 @@ function piUiResponse( export type PiAdapterV2DriverEnv = | ChildProcessSpawner.ChildProcessSpawner | FileSystem.FileSystem - | IdAllocatorV2 - | ServerConfig; + | IdAllocator.IdAllocatorV2 + | ServerConfig.ServerConfig; export const PiAdapterV2Driver: ProviderAdapterDriver = { driverKind: PI_DRIVER_KIND, @@ -2976,8 +2955,8 @@ export const PiAdapterV2Driver: ProviderAdapterDriver = Layer.effect( - ProviderAdapterV2, - Effect.gen(function* () { - const hostEnvironment = yield* HostProcessEnvironment; - const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; - const fileSystem = yield* FileSystem.FileSystem; - const idAllocator = yield* IdAllocatorV2; - const serverConfig = yield* ServerConfig; - return makePiAdapterV2({ - instanceId: PI_DEFAULT_INSTANCE_ID, - settings: DEFAULT_PI_SETTINGS, - environment: hostEnvironment, - spawner, - fileSystem, - idAllocator, - serverConfig, - }); - }), -); +const layer: Layer.Layer = + Layer.effect( + ProviderAdapter.ProviderAdapterV2, + Effect.gen(function* () { + const hostEnvironment = yield* HostProcessEnvironment; + const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; + const fileSystem = yield* FileSystem.FileSystem; + const idAllocator = yield* IdAllocator.IdAllocatorV2; + const serverConfig = yield* ServerConfig.ServerConfig; + return makePiAdapterV2({ + instanceId: PI_DEFAULT_INSTANCE_ID, + settings: DEFAULT_PI_SETTINGS, + environment: hostEnvironment, + spawner, + fileSystem, + idAllocator, + serverConfig, + }); + }), + ); From b0a64ff4d8ae6468e589df8b8df734611dc0e3a8 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 30 Sep 2026 01:35:58 -0700 Subject: [PATCH 3/4] refactor(server): import services as namespaces in the orchestration-v2 testkit Replace named tag and layer imports (and layer aliases) with module namespaces in orchestration-v2/testkit, outside fixtures/. Imports and call sites only. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../ClaudeReplayFixtures.integration.test.ts | 8 +- ...rchestratorReplayFixtures.contract.test.ts | 4 +- ...estratorReplayFixtures.integration.test.ts | 4 +- ...estratorReplayRecovery.integration.test.ts | 8 +- ...yRestartBackgroundNote.integration.test.ts | 8 +- .../testkit/OrchestratorScenario.ts | 48 +++-- .../testkit/ProviderReplayHarness.ts | 200 +++++++++--------- .../ProviderSwitch.integration.test.ts | 151 +++++++------ .../testkit/ThreadFork.integration.test.ts | 26 +-- .../ThreadMergeBack.integration.test.ts | 16 +- 10 files changed, 242 insertions(+), 231 deletions(-) diff --git a/apps/server/src/orchestration-v2/testkit/ClaudeReplayFixtures.integration.test.ts b/apps/server/src/orchestration-v2/testkit/ClaudeReplayFixtures.integration.test.ts index 3753206c94df..fff9e79a8909 100644 --- a/apps/server/src/orchestration-v2/testkit/ClaudeReplayFixtures.integration.test.ts +++ b/apps/server/src/orchestration-v2/testkit/ClaudeReplayFixtures.integration.test.ts @@ -11,8 +11,8 @@ import * as Effect from "effect/Effect"; import { classifyClaudeNativeTool } from "../Adapters/ClaudeAdapterV2.ts"; import { ClaudeOrchestratorReplayHarness } from "../Adapters/ClaudeAdapterV2.testkit.ts"; -import { layer as idAllocatorLayer } from "../IdAllocator.ts"; -import { OrchestratorV2 } from "../Orchestrator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; +import * as Orchestrator from "../Orchestrator.ts"; import { userFacingDispatchErrorMessage } from "../UserFacingErrors.ts"; import { provideDeterministicTestRuntime } from "./DeterministicRuntime.ts"; import { ORCHESTRATOR_REPLAY_FIXTURES } from "./fixtures/index.ts"; @@ -146,7 +146,7 @@ describe("Claude Agent SDK replay fixtures", () => { fixtureInput: subagentInput(), driver: ProviderDriverKind.make("claudeAgent"), modelSelection: CLAUDE_MODEL_SELECTION, - }).pipe(Effect.provide(idAllocatorLayer), provideDeterministicTestRuntime); + }).pipe(Effect.provide(IdAllocator.layer), provideDeterministicTestRuntime); const scenario = { name: "subagent/claudeAgent:read-only-child", transcript, @@ -156,7 +156,7 @@ describe("Claude Agent SDK replay fixtures", () => { }; yield* Effect.gen(function* () { const result = yield* runOrchestratorV2Scenario(scenario); - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const child = [...result.projections.values()].find((projection) => isProviderNativeSubagentThread(projection.thread), ); diff --git a/apps/server/src/orchestration-v2/testkit/OrchestratorReplayFixtures.contract.test.ts b/apps/server/src/orchestration-v2/testkit/OrchestratorReplayFixtures.contract.test.ts index ab0dc83f8a21..e0cd30fe5ba3 100644 --- a/apps/server/src/orchestration-v2/testkit/OrchestratorReplayFixtures.contract.test.ts +++ b/apps/server/src/orchestration-v2/testkit/OrchestratorReplayFixtures.contract.test.ts @@ -4,7 +4,7 @@ import { OrchestrationV2Command, ProviderInstanceId } from "@t3tools/contracts"; import * as Effect from "effect/Effect"; import * as Schema from "effect/Schema"; -import { layer as idAllocatorLayer } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { provideDeterministicTestRuntime } from "./DeterministicRuntime.ts"; import { ORCHESTRATOR_REPLAY_FIXTURES } from "./fixtures/index.ts"; import { materializeFixtureInput } from "./fixtures/shared.ts"; @@ -51,7 +51,7 @@ describe("orchestrator replay fixture contract", () => { fixtureInput: fixture.buildInput(), driver: provider.driver, modelSelection: provider.modelSelection, - }).pipe(Effect.provide(idAllocatorLayer), provideDeterministicTestRuntime); + }).pipe(Effect.provide(IdAllocator.layer), provideDeterministicTestRuntime); const firstCommand = materialized.commands[0]; assert.equal(transcript.scenario, provider.recordedScenario ?? fixture.name); diff --git a/apps/server/src/orchestration-v2/testkit/OrchestratorReplayFixtures.integration.test.ts b/apps/server/src/orchestration-v2/testkit/OrchestratorReplayFixtures.integration.test.ts index b74234d12209..ea39e935f944 100644 --- a/apps/server/src/orchestration-v2/testkit/OrchestratorReplayFixtures.integration.test.ts +++ b/apps/server/src/orchestration-v2/testkit/OrchestratorReplayFixtures.integration.test.ts @@ -12,7 +12,7 @@ import { AcpRegistryOrchestratorReplayHarness } from "../Adapters/AcpRegistryAda import { GrokOrchestratorReplayHarness } from "../Adapters/GrokAdapterV2.testkit.ts"; import { OpenCodeOrchestratorReplayHarness } from "../Adapters/OpenCodeAdapterV2.testkit.ts"; import { PiOrchestratorReplayHarness } from "../Adapters/PiAdapterV2.testkit.ts"; -import { layer as idAllocatorLayer } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { provideDeterministicTestRuntime } from "./DeterministicRuntime.ts"; import { ORCHESTRATOR_REPLAY_FIXTURES } from "./fixtures/index.ts"; import { messageRestartInput } from "./fixtures/message_steering/input.ts"; @@ -99,7 +99,7 @@ const runFixtureProvider = Effect.fn("runOrchestratorReplayFixture")(function* < fixtureInput, driver: input.driver.driver, modelSelection: input.driver.modelSelection, - }).pipe(Effect.provide(idAllocatorLayer), provideDeterministicTestRuntime); + }).pipe(Effect.provide(IdAllocator.layer), provideDeterministicTestRuntime); const scenario = { name: `${input.fixtureName}/${input.driver.driver}`, transcript, diff --git a/apps/server/src/orchestration-v2/testkit/OrchestratorReplayRecovery.integration.test.ts b/apps/server/src/orchestration-v2/testkit/OrchestratorReplayRecovery.integration.test.ts index f2af0e44b9cc..56a96a031f40 100644 --- a/apps/server/src/orchestration-v2/testkit/OrchestratorReplayRecovery.integration.test.ts +++ b/apps/server/src/orchestration-v2/testkit/OrchestratorReplayRecovery.integration.test.ts @@ -22,7 +22,7 @@ import { makeCursorAgentSdkReplayRunner, makeCursorProviderAdapterRegistryReplayLayer, } from "../Adapters/CursorAdapterV2.testkit.ts"; -import { layer as idAllocatorLayer } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { makeSqlitePersistenceLive } from "../../persistence/Layers/Sqlite.ts"; import { provideDeterministicTestRuntime } from "./DeterministicRuntime.ts"; import { @@ -291,7 +291,7 @@ describe("orchestrator replay recovery", () => { assert.lengthOf(projection.providerThreads, 1); }).pipe( provideDeterministicTestRuntime, - Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer)), + Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer)), ), ), ); @@ -307,7 +307,7 @@ describe("orchestrator replay recovery", () => { yield* runner.assertComplete; }).pipe( provideDeterministicTestRuntime, - Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer)), + Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer)), ), ), ); @@ -417,7 +417,7 @@ describe("orchestrator replay recovery", () => { ); }).pipe( provideDeterministicTestRuntime, - Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer)), + Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer)), ), ), ); diff --git a/apps/server/src/orchestration-v2/testkit/OrchestratorReplayRestartBackgroundNote.integration.test.ts b/apps/server/src/orchestration-v2/testkit/OrchestratorReplayRestartBackgroundNote.integration.test.ts index d03fabd7521c..fc61c61646bc 100644 --- a/apps/server/src/orchestration-v2/testkit/OrchestratorReplayRestartBackgroundNote.integration.test.ts +++ b/apps/server/src/orchestration-v2/testkit/OrchestratorReplayRestartBackgroundNote.integration.test.ts @@ -10,7 +10,7 @@ import { ClaudeOrchestratorReplayHarness, makeClaudeRestartReplayHarness, } from "../Adapters/ClaudeAdapterV2.testkit.ts"; -import { IdAllocatorV2, layer as idAllocatorLayer } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { makeSqlitePersistenceLive } from "../../persistence/Layers/Sqlite.ts"; import { provideDeterministicTestRuntime } from "./DeterministicRuntime.ts"; import { CLAUDE_BACKGROUND_SUBAGENT_AFTER_ROOT_PROMPT } from "./fixtures/claude_background_subagent_after_root/input.ts"; @@ -163,7 +163,7 @@ const runRestart = Effect.fn("runRestart")(function* (input: { { type: "await_run_status" as const, threadId, - runId: (yield* IdAllocatorV2).derive.run({ threadId, ordinal: 2 }), + runId: (yield* IdAllocator.IdAllocatorV2).derive.run({ threadId, ordinal: 2 }), status: "completed" as const, }, ] @@ -235,7 +235,7 @@ describe("restart-cancelled background work", () => { ]); }).pipe( provideDeterministicTestRuntime, - Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer)), + Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer)), ), ), ); @@ -263,7 +263,7 @@ describe("restart-cancelled background work", () => { ]); }).pipe( provideDeterministicTestRuntime, - Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer)), + Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer)), ), ), ); diff --git a/apps/server/src/orchestration-v2/testkit/OrchestratorScenario.ts b/apps/server/src/orchestration-v2/testkit/OrchestratorScenario.ts index 8b345aee9d4a..e3381514fb9c 100644 --- a/apps/server/src/orchestration-v2/testkit/OrchestratorScenario.ts +++ b/apps/server/src/orchestration-v2/testkit/OrchestratorScenario.ts @@ -22,7 +22,7 @@ import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; import { TestClock } from "effect/testing"; -import { OrchestratorV2, type OrchestratorV2Error } from "../Orchestrator.ts"; +import * as Orchestrator from "../Orchestrator.ts"; import type { ProviderReplayGate } from "./ProviderReplayGate.testkit.ts"; export type OrchestratorV2ScenarioStep = @@ -253,12 +253,12 @@ export function runOrchestratorV2Scenario( } = {}, ): Effect.Effect< OrchestratorV2ScenarioResult, - OrchestratorV2Error | OrchestratorV2ScenarioStepError, - OrchestratorV2 + Orchestrator.OrchestratorV2Error | OrchestratorV2ScenarioStepError, + Orchestrator.OrchestratorV2 > { return Effect.scoped( Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; const storedEventGroups: Array> = []; const observedStoredEvents = yield* Ref.make>([]); yield* orchestrator.streamStoredEvents.pipe( @@ -269,7 +269,7 @@ export function runOrchestratorV2Scenario( ); const backgroundDispatches = new Map< string, - Fiber.Fiber, OrchestratorV2Error> + Fiber.Fiber, Orchestrator.OrchestratorV2Error> >(); const capturedShellSnapshots = new Map(); let anonymousBackgroundDispatchIndex = 0; @@ -294,7 +294,7 @@ export function runOrchestratorV2Scenario( deadlineAt = scenarioWaitDeadline(), ): Effect.Effect< OrchestrationV2RuntimeRequest, - OrchestratorV2Error | OrchestratorV2ScenarioStepError, + Orchestrator.OrchestratorV2Error | OrchestratorV2ScenarioStepError, never > => Effect.gen(function* () { @@ -318,7 +318,11 @@ export function runOrchestratorV2Scenario( threadId: ThreadId, attemptsRemaining = SCENARIO_WAIT_ATTEMPTS, deadlineAt = scenarioWaitDeadline(), - ): Effect.Effect => + ): Effect.Effect< + void, + Orchestrator.OrchestratorV2Error | OrchestratorV2ScenarioStepError, + never + > => Effect.gen(function* () { const projection = yield* orchestrator.getThreadProjection(threadId); if (!hasActiveRun(projection)) { @@ -349,7 +353,11 @@ export function runOrchestratorV2Scenario( runId: OrchestrationV2Run["id"], attemptsRemaining = SCENARIO_WAIT_ATTEMPTS, deadlineAt = scenarioWaitDeadline(), - ): Effect.Effect => + ): Effect.Effect< + void, + Orchestrator.OrchestratorV2Error | OrchestratorV2ScenarioStepError, + never + > => Effect.gen(function* () { const projection = yield* orchestrator.getThreadProjection(threadId); const run = projection.runs.find((candidate) => candidate.id === runId); @@ -378,7 +386,11 @@ export function runOrchestratorV2Scenario( status: OrchestrationV2Run["status"], attemptsRemaining = SCENARIO_WAIT_ATTEMPTS, deadlineAt = scenarioWaitDeadline(), - ): Effect.Effect => + ): Effect.Effect< + void, + Orchestrator.OrchestratorV2Error | OrchestratorV2ScenarioStepError, + never + > => Effect.gen(function* () { const projection = yield* orchestrator.getThreadProjection(threadId); const run = projection.runs.find((candidate) => candidate.id === runId); @@ -407,7 +419,11 @@ export function runOrchestratorV2Scenario( itemType: OrchestrationV2TurnItem["type"], attemptsRemaining = SCENARIO_WAIT_ATTEMPTS, deadlineAt = scenarioWaitDeadline(), - ): Effect.Effect => + ): Effect.Effect< + void, + Orchestrator.OrchestratorV2Error | OrchestratorV2ScenarioStepError, + never + > => Effect.gen(function* () { const projection = yield* orchestrator.getThreadProjection(threadId); const hasTurnItem = projection.turnItems.some( @@ -437,7 +453,11 @@ export function runOrchestratorV2Scenario( threadId: ThreadId, runId: OrchestrationV2Run["id"], attemptsRemaining = SCENARIO_WAIT_ATTEMPTS, - ): Effect.Effect => + ): Effect.Effect< + void, + Orchestrator.OrchestratorV2Error | OrchestratorV2ScenarioStepError, + never + > => Effect.gen(function* () { const projection = yield* orchestrator.getThreadProjection(threadId); const run = projection.runs.find((candidate) => candidate.id === runId); @@ -476,7 +496,11 @@ export function runOrchestratorV2Scenario( threadId: ThreadId, providerThreadId: NonNullable, attemptsRemaining = SCENARIO_WAIT_ATTEMPTS, - ): Effect.Effect => + ): Effect.Effect< + void, + Orchestrator.OrchestratorV2Error | OrchestratorV2ScenarioStepError, + never + > => Effect.gen(function* () { const projection = yield* orchestrator.getThreadProjection(threadId); const providerThread = projection.providerThreads.find( diff --git a/apps/server/src/orchestration-v2/testkit/ProviderReplayHarness.ts b/apps/server/src/orchestration-v2/testkit/ProviderReplayHarness.ts index eb2fc5e373f5..2fa1345528f9 100644 --- a/apps/server/src/orchestration-v2/testkit/ProviderReplayHarness.ts +++ b/apps/server/src/orchestration-v2/testkit/ProviderReplayHarness.ts @@ -12,55 +12,45 @@ import type { MigrationError } from "effect/unstable/sql/Migrator"; import type { SqlError } from "effect/unstable/sql/SqlError"; import * as CheckpointStore from "../../checkpointing/CheckpointStore.ts"; -import { ServerConfig } from "../../config.ts"; +import * as ServerConfig from "../../config.ts"; import { SqlitePersistenceMemory } from "../../persistence/Layers/Sqlite.ts"; -import { ServerSettingsService } from "../../serverSettings.ts"; -import { ThreadManagementService } from "../ThreadManagementService.ts"; -import { layer as mcpSessionRegistryTestLayer } from "../../mcp/McpSessionRegistry.testkit.ts"; +import * as ServerSettings from "../../serverSettings.ts"; +import * as ThreadManagementService from "../ThreadManagementService.ts"; +import * as McpSessionRegistryTestkit from "../../mcp/McpSessionRegistry.testkit.ts"; import * as VcsDriverRegistry from "../../vcs/VcsDriverRegistry.ts"; import * as VcsProcess from "../../vcs/VcsProcess.ts"; -import { layer as checkpointCaptureServiceLayer } from "../CheckpointCaptureService.ts"; -import { layer as checkpointServiceLayer } from "../CheckpointService.ts"; -import { layer as checkpointRollbackServiceLayer } from "../CheckpointRollbackService.ts"; -import { layer as commandPolicyLayer } from "../CommandPolicy.ts"; -import { layer as commandReceiptStoreLayer } from "../CommandReceiptStore.ts"; -import { layer as contextHandoffServiceLayer } from "../ContextHandoffService.ts"; -import { layer as effectOutboxLayer } from "../EffectOutbox.ts"; -import { - executorLayer as effectExecutorLayer, - layer as effectWorkerLayer, - runDaemon as runEffectWorkerDaemon, - OrchestrationEffectWorkerV2, -} from "../EffectWorker.ts"; -import { EventSinkV2, layerFromStores as eventSinkLayer } from "../EventSink.ts"; -import { layer as eventStoreLayer } from "../EventStore.ts"; -import { layer as idAllocatorLayer } from "../IdAllocator.ts"; -import { layer as orchestratorLayer } from "../Orchestrator.ts"; -import { layer as projectionStoreLayer } from "../ProjectionStore.ts"; -import { layer as projectStoreLayer } from "../ProjectStore.ts"; -import { OrchestratorV2, type OrchestratorV2Error } from "../Orchestrator.ts"; -import { ProviderAdapterRegistryV2 } from "../ProviderAdapterRegistry.ts"; -import { ProviderAuthService } from "../../provider/Services/ProviderAuthService.ts"; -import { layer as providerContinuationRequestsLayer } from "../ProviderContinuationRequests.ts"; -import { workerLive as providerContinuationWorkerLive } from "../ProviderContinuationService.ts"; -import { layer as providerEventIngestorLayer } from "../ProviderEventIngestor.ts"; +import * as CheckpointCaptureService from "../CheckpointCaptureService.ts"; +import * as CheckpointService from "../CheckpointService.ts"; +import * as CheckpointRollbackService from "../CheckpointRollbackService.ts"; +import * as CommandPolicy from "../CommandPolicy.ts"; +import * as CommandReceiptStore from "../CommandReceiptStore.ts"; +import * as ContextHandoffService from "../ContextHandoffService.ts"; +import * as EffectOutbox from "../EffectOutbox.ts"; +import * as EffectWorker from "../EffectWorker.ts"; +import * as EventSink from "../EventSink.ts"; +import * as EventStore from "../EventStore.ts"; +import * as IdAllocator from "../IdAllocator.ts"; +import * as Orchestrator from "../Orchestrator.ts"; +import * as ProjectionStore from "../ProjectionStore.ts"; +import * as ProjectStore from "../ProjectStore.ts"; +import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; +import * as ProviderAuthService from "../../provider/Services/ProviderAuthService.ts"; +import * as ProviderContinuationRequests from "../ProviderContinuationRequests.ts"; +import * as ProviderContinuationService from "../ProviderContinuationService.ts"; +import * as ProviderEventIngestor from "../ProviderEventIngestor.ts"; import * as ProviderRuntimeRecoveryService from "../ProviderRuntimeRecoveryService.ts"; -import { layerWithOptions as providerSessionManagerLayerWithOptions } from "../ProviderSessionManager.ts"; -import { layer as providerSwitchServiceLayer } from "../ProviderSwitchService.ts"; -import { layer as providerTurnControlServiceLayer } from "../ProviderTurnControlService.ts"; -import { layer as providerTurnStartServiceLayer } from "../ProviderTurnStartService.ts"; +import * as ProviderSessionManager from "../ProviderSessionManager.ts"; +import * as ProviderSwitchService from "../ProviderSwitchService.ts"; +import * as ProviderTurnControlService from "../ProviderTurnControlService.ts"; +import * as ProviderTurnStartService from "../ProviderTurnStartService.ts"; import { worktreeRepairDependenciesTestLayer } from "../ProviderTurnStartService.testkit.ts"; -import { layer as runExecutionServiceLayer } from "../RunExecutionService.ts"; -import { layer as runFinalizationServiceLayer } from "../RunFinalizationService.ts"; -import { ThreadTitleRegenerationService } from "../ThreadTitleRegenerationService.ts"; -import { - layer as runtimePolicyLayer, - layerWithOverride as runtimePolicyLayerWithOverride, - type RuntimePolicyV2Override, -} from "../RuntimePolicy.ts"; -import { layer as turnItemPositionStoreLayer } from "../TurnItemPositionStore.ts"; -import { layer as runtimeRequestServiceLayer } from "../RuntimeRequestService.ts"; -import { layer as threadForkServiceLayer } from "../ThreadForkService.ts"; +import * as RunExecutionService from "../RunExecutionService.ts"; +import * as RunFinalizationService from "../RunFinalizationService.ts"; +import * as ThreadTitleRegenerationService from "../ThreadTitleRegenerationService.ts"; +import * as RuntimePolicy from "../RuntimePolicy.ts"; +import * as TurnItemPositionStore from "../TurnItemPositionStore.ts"; +import * as RuntimeRequestService from "../RuntimeRequestService.ts"; +import * as ThreadForkService from "../ThreadForkService.ts"; import { runOrchestratorV2Scenario, type OrchestratorV2ScenarioStepError, @@ -72,7 +62,7 @@ import { makeProviderReplayGate, type ProviderReplayGate } from "./ProviderRepla export function makeReplayServerConfig( scenario: string, ): Effect.Effect< - ServerConfig["Service"], + ServerConfig.ServerConfig["Service"], PlatformError.PlatformError, FileSystem.FileSystem | Path.Path > { @@ -161,7 +151,7 @@ export interface OrchestratorV2ProviderReplayScenario< Transcript extends ProviderReplayTranscript = ProviderReplayTranscript, > extends OrchestratorV2Scenario { readonly transcript: Transcript; - readonly runtimePolicyOverride?: RuntimePolicyV2Override; + readonly runtimePolicyOverride?: RuntimePolicy.RuntimePolicyV2Override; } export interface OrchestratorV2ProviderReplayHarness< @@ -175,7 +165,7 @@ export interface OrchestratorV2ProviderReplayHarness< readonly makeProviderAdapterRegistryLayer: ( transcript: Transcript, options?: { readonly replayGate?: ProviderReplayGate }, - ) => Layer.Layer; + ) => Layer.Layer; } export function runOrchestratorV2ProviderReplayScenario< @@ -200,7 +190,7 @@ export function runOrchestratorV2ProviderReplayScenario< } = {}, ): Effect.Effect< OrchestratorV2ScenarioResult, - | OrchestratorV2Error + | Orchestrator.OrchestratorV2Error | OrchestratorV2ScenarioStepError | Error | MigrationError @@ -245,7 +235,7 @@ export function makeOrchestratorV2ProviderReplayLayer< readonly replayGate?: ProviderReplayGate; } = {}, ): Layer.Layer< - OrchestratorV2 | OrchestrationEffectWorkerV2 | EventSinkV2, + Orchestrator.OrchestratorV2 | EffectWorker.OrchestrationEffectWorkerV2 | EventSink.EventSinkV2, Error | MigrationError | PlatformError.PlatformError | SqlError > { const registryLayer = harness.makeProviderAdapterRegistryLayer( @@ -257,7 +247,7 @@ export function makeOrchestratorV2ProviderReplayLayer< export function makeOrchestratorV2ReplayLayerWithRegistry( scenario: Pick, - registryLayer: Layer.Layer, + registryLayer: Layer.Layer, options: { readonly databaseLayer?: Layer.Layer< SqlClient.SqlClient, @@ -273,45 +263,45 @@ export function makeOrchestratorV2ReplayLayerWithRegistry( readonly continueThreadsAfterServerUpdate?: boolean; } = {}, ): Layer.Layer< - OrchestratorV2 | OrchestrationEffectWorkerV2 | EventSinkV2, + Orchestrator.OrchestratorV2 | EffectWorker.OrchestrationEffectWorkerV2 | EventSink.EventSinkV2, Error | MigrationError | PlatformError.PlatformError | SqlError > { const serverConfigLayer = Layer.effect( - ServerConfig, + ServerConfig.ServerConfig, makeReplayServerConfig(scenario.name).pipe(Effect.orDie), ).pipe(Layer.provide(NodeServices.layer)); const runtimeLayer = scenario.runtimePolicyOverride === undefined - ? runtimePolicyLayer - : runtimePolicyLayerWithOverride(scenario.runtimePolicyOverride).pipe( - Layer.provide(runtimePolicyLayer), + ? RuntimePolicy.layer + : RuntimePolicy.layerWithOverride(scenario.runtimePolicyOverride).pipe( + Layer.provide(RuntimePolicy.layer), ); const databaseLayer = options.databaseLayer ?? SqlitePersistenceMemory; // One queue shared by the adapters, the orchestrator, and the worker, like // runtimeLayer.ts; layer memoization keeps it a single instance. const continuationRequestsLayer = - options.runContinuationWorker === true ? providerContinuationRequestsLayer : Layer.empty; + options.runContinuationWorker === true ? ProviderContinuationRequests.layer : Layer.empty; const providedRegistryLayer = registryLayer.pipe(Layer.provide(continuationRequestsLayer)); - const serverSettingsLayer = ServerSettingsService.layerTest({ + const serverSettingsLayer = ServerSettings.layerTest({ responseStreamingMode: "turn", ...(options.continueThreadsAfterServerUpdate === undefined ? {} : { continueThreadsAfterServerUpdate: options.continueThreadsAfterServerUpdate }), }).pipe(Layer.orDie); const storesLayer = Layer.mergeAll( - eventStoreLayer, - projectionStoreLayer, - projectStoreLayer, - commandReceiptStoreLayer, - effectOutboxLayer, - turnItemPositionStoreLayer, + EventStore.layer, + ProjectionStore.layer, + ProjectStore.layer, + CommandReceiptStore.layer, + EffectOutbox.layer, + TurnItemPositionStore.layer, ).pipe(Layer.provide(databaseLayer)); - const eventSinkProvided = eventSinkLayer.pipe( + const eventSinkProvided = EventSink.layerFromStores.pipe( Layer.provide(Layer.mergeAll(storesLayer, databaseLayer)), ); - const commandReceiptStoreProvided = commandReceiptStoreLayer.pipe(Layer.provide(databaseLayer)); - const providerEventIngestorProvided = providerEventIngestorLayer.pipe( - Layer.provide(Layer.mergeAll(storesLayer, eventSinkProvided, idAllocatorLayer)), + const commandReceiptStoreProvided = CommandReceiptStore.layer.pipe(Layer.provide(databaseLayer)); + const providerEventIngestorProvided = ProviderEventIngestor.layer.pipe( + Layer.provide(Layer.mergeAll(storesLayer, eventSinkProvided, IdAllocator.layer)), ); const vcsDriverRegistryLayer = VcsDriverRegistry.layer.pipe( Layer.provide(VcsProcess.layer), @@ -322,96 +312,100 @@ export function makeOrchestratorV2ReplayLayerWithRegistry( Layer.provide(vcsDriverRegistryLayer), Layer.provide(NodeServices.layer), ); - const checkpointServiceProvided = checkpointServiceLayer.pipe( - Layer.provide(Layer.mergeAll(checkpointStoreLayer, idAllocatorLayer)), + const checkpointServiceProvided = CheckpointService.layer.pipe( + Layer.provide(Layer.mergeAll(checkpointStoreLayer, IdAllocator.layer)), ); - const contextHandoffServiceProvided = contextHandoffServiceLayer.pipe( - Layer.provide(idAllocatorLayer), + const contextHandoffServiceProvided = ContextHandoffService.layer.pipe( + Layer.provide(IdAllocator.layer), ); const persistenceLayer = Layer.mergeAll( storesLayer, eventSinkProvided, commandReceiptStoreProvided, - idAllocatorLayer, + IdAllocator.layer, providerEventIngestorProvided, ); - const providerSessionManagerProvided = providerSessionManagerLayerWithOptions({ + const providerSessionManagerProvided = ProviderSessionManager.layerWithOptions({ configureMcp: false, }).pipe( Layer.provide( Layer.mergeAll( providedRegistryLayer, eventSinkProvided, - idAllocatorLayer, - mcpSessionRegistryTestLayer, + IdAllocator.layer, + McpSessionRegistryTestkit.layer, providerEventIngestorProvided, storesLayer, ), ), ); - const providerSwitchServiceProvided = providerSwitchServiceLayer.pipe( + const providerSwitchServiceProvided = ProviderSwitchService.layer.pipe( Layer.provide(providedRegistryLayer), ); - const runExecutionServiceProvided = runExecutionServiceLayer.pipe( + const runExecutionServiceProvided = RunExecutionService.layer.pipe( Layer.provide( Layer.mergeAll( checkpointServiceProvided, eventSinkProvided, - idAllocatorLayer, + IdAllocator.layer, providerEventIngestorProvided, serverSettingsLayer, ), ), ); - const providerTurnStartServiceProvided = providerTurnStartServiceLayer.pipe( + const providerTurnStartServiceProvided = ProviderTurnStartService.layer.pipe( Layer.provide( Layer.mergeAll( contextHandoffServiceProvided, eventSinkProvided, - idAllocatorLayer, + IdAllocator.layer, storesLayer, providerSessionManagerProvided, - Layer.mock(ProviderAuthService)({ tryHandlePromptCommand: () => Effect.succeed(false) }), + Layer.mock(ProviderAuthService.ProviderAuthService)({ + tryHandlePromptCommand: () => Effect.succeed(false), + }), runExecutionServiceProvided, runtimeLayer, ), ), ); - const providerTurnControlServiceProvided = providerTurnControlServiceLayer.pipe( + const providerTurnControlServiceProvided = ProviderTurnControlService.layer.pipe( Layer.provide(Layer.merge(storesLayer, providerSessionManagerProvided)), ); - const runtimeRequestServiceProvided = runtimeRequestServiceLayer.pipe( + const runtimeRequestServiceProvided = RuntimeRequestService.layer.pipe( Layer.provide(Layer.merge(storesLayer, providerSessionManagerProvided)), ); - const checkpointRollbackServiceProvided = checkpointRollbackServiceLayer.pipe( + const checkpointRollbackServiceProvided = CheckpointRollbackService.layer.pipe( Layer.provide( Layer.mergeAll( checkpointServiceProvided, eventSinkProvided, - idAllocatorLayer, + IdAllocator.layer, storesLayer, providerSessionManagerProvided, runtimeLayer, ), ), ); - const checkpointCaptureServiceProvided = checkpointCaptureServiceLayer.pipe( + const checkpointCaptureServiceProvided = CheckpointCaptureService.layer.pipe( Layer.provide( - Layer.mergeAll(checkpointServiceProvided, eventSinkProvided, idAllocatorLayer, storesLayer), + Layer.mergeAll(checkpointServiceProvided, eventSinkProvided, IdAllocator.layer, storesLayer), ), ); - const runFinalizationServiceProvided = runFinalizationServiceLayer.pipe( + const runFinalizationServiceProvided = RunFinalizationService.layer.pipe( Layer.provide(Layer.merge(checkpointCaptureServiceProvided, storesLayer)), ); const threadTitleRegenerationTestLayer = Layer.succeed( - ThreadTitleRegenerationService, - ThreadTitleRegenerationService.of({ execute: () => Effect.void }), + ThreadTitleRegenerationService.ThreadTitleRegenerationService, + ThreadTitleRegenerationService.ThreadTitleRegenerationService.of({ + execute: () => Effect.void, + }), ); - const orchestratorProvided = orchestratorLayer.pipe( + const orchestratorProvided = Orchestrator.layer.pipe( Layer.provide( Layer.mergeAll( checkpointServiceProvided, - commandPolicyLayer, + CommandPolicy.layer, contextHandoffServiceProvided, persistenceLayer, providedRegistryLayer, @@ -420,14 +414,14 @@ export function makeOrchestratorV2ReplayLayerWithRegistry( providerSessionManagerProvided, providerSwitchServiceProvided, runExecutionServiceProvided, - threadForkServiceLayer, + ThreadForkService.layer, ), ), ); const threadManagementProvided = Layer.unwrap( Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - return Layer.mock(ThreadManagementService)({ + const orchestrator = yield* Orchestrator.OrchestratorV2; + return Layer.mock(ThreadManagementService.ThreadManagementService)({ dispatch: orchestrator.dispatch, getThreadRecords: orchestrator.getThreadRecords, getThreadProjection: orchestrator.getThreadProjection, @@ -436,13 +430,13 @@ export function makeOrchestratorV2ReplayLayerWithRegistry( ).pipe(Layer.provide(orchestratorProvided)); const continuationWorkerProvided = options.runContinuationWorker === true - ? providerContinuationWorkerLive.pipe( + ? ProviderContinuationService.workerLive.pipe( Layer.provide( - Layer.mergeAll(continuationRequestsLayer, threadManagementProvided, idAllocatorLayer), + Layer.mergeAll(continuationRequestsLayer, threadManagementProvided, IdAllocator.layer), ), ) : Layer.empty; - const effectExecutorProvided = effectExecutorLayer.pipe( + const effectExecutorProvided = EffectWorker.executorLayer.pipe( Layer.provide( Layer.mergeAll( runFinalizationServiceProvided, @@ -457,7 +451,7 @@ export function makeOrchestratorV2ReplayLayerWithRegistry( ), ), ); - const effectWorkerProvided = effectWorkerLayer.pipe( + const effectWorkerProvided = EffectWorker.layer.pipe( Layer.provide(Layer.merge(storesLayer, effectExecutorProvided)), ); const replayRuntime = Layer.mergeAll( @@ -487,15 +481,15 @@ export function makeOrchestratorV2ReplayLayerWithRegistry( ).pipe( Layer.provide(ProviderRuntimeRecoveryService.layer), Layer.provide( - Layer.mergeAll(storesLayer, eventSinkProvided, idAllocatorLayer, serverSettingsLayer), + Layer.mergeAll(storesLayer, eventSinkProvided, IdAllocator.layer, serverSettingsLayer), ), ) : Layer.empty; return Layer.effect( - OrchestratorV2, + Orchestrator.OrchestratorV2, Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - yield* runEffectWorkerDaemon.pipe(Effect.forkScoped); + const orchestrator = yield* Orchestrator.OrchestratorV2; + yield* EffectWorker.runDaemon.pipe(Effect.forkScoped); return orchestrator; }), ).pipe(Layer.provideMerge(replayRuntime), Layer.provide(startupRecovery)); diff --git a/apps/server/src/orchestration-v2/testkit/ProviderSwitch.integration.test.ts b/apps/server/src/orchestration-v2/testkit/ProviderSwitch.integration.test.ts index f6ab86fee1c8..cba4cc6fbf19 100644 --- a/apps/server/src/orchestration-v2/testkit/ProviderSwitch.integration.test.ts +++ b/apps/server/src/orchestration-v2/testkit/ProviderSwitch.integration.test.ts @@ -41,21 +41,14 @@ import { } from "../Adapters/CodexAdapterV2.ts"; import { AcpProviderCapabilitiesV2 } from "../Adapters/AcpAdapterV2.ts"; import { CursorProviderCapabilitiesV2 } from "../Adapters/CursorAdapterV2.ts"; -import { layer as eventSinkLayer } from "../EventSink.ts"; -import { EventSinkV2, EventSinkWriteError } from "../EventSink.ts"; -import { layer as eventStoreLayer } from "../EventStore.ts"; -import { - LegacyV1ThreadImporter, - layer as legacyV1ThreadImporterLayer, -} from "../legacy/LegacyV1ThreadImporter.ts"; -import { OrchestratorDispatchError, OrchestratorV2 } from "../Orchestrator.ts"; -import { OrchestrationEffectWorkerV2 } from "../EffectWorker.ts"; -import { EffectOutboxV2, layer as effectOutboxLayer } from "../EffectOutbox.ts"; -import { - ProjectionMaintenanceV2, - layer as projectionMaintenanceLayer, -} from "../ProjectionMaintenance.ts"; -import { layer as projectionStoreLayer } from "../ProjectionStore.ts"; +import * as EventSink from "../EventSink.ts"; +import * as EventStore from "../EventStore.ts"; +import * as LegacyV1ThreadImporter from "../legacy/LegacyV1ThreadImporter.ts"; +import * as Orchestrator from "../Orchestrator.ts"; +import * as EffectWorker from "../EffectWorker.ts"; +import * as EffectOutbox from "../EffectOutbox.ts"; +import * as ProjectionMaintenance from "../ProjectionMaintenance.ts"; +import * as ProjectionStore from "../ProjectionStore.ts"; import { type ProviderAdapterV2Event, type ProviderAdapterV2HistoricalContext, @@ -63,11 +56,7 @@ import { type ProviderAdapterV2Shape, type ProviderAdapterV2SessionRuntime, } from "../ProviderAdapter.ts"; -import { makeLayer as makeProviderAdapterRegistryLayer } from "../ProviderAdapterRegistry.ts"; -import { - ProviderAdapterRegistryLookupError, - ProviderAdapterRegistryV2, -} from "../ProviderAdapterRegistry.ts"; +import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; import { makeProviderFailure } from "../ProviderFailure.ts"; import { CLAUDE_MODEL_SELECTION, @@ -369,7 +358,7 @@ function makeTestAdapter(input: { const waitForIdle = Effect.fn("ProviderSwitchTest.waitForIdle")(function* ( targetThreadId: ThreadId, ) { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; for (let attempt = 0; attempt < 1_000; attempt += 1) { const projection = yield* orchestrator.getThreadProjection(targetThreadId); if ( @@ -448,7 +437,7 @@ describe("orchestration v2 provider switching", () => { ? { options: [{ id: "contextWindow", value: "1m" }] } : { model: `${CLAUDE_MODEL_SELECTION.model}-large` }), }; - const registry = makeProviderAdapterRegistryLayer([ + const registry = ProviderAdapterRegistry.makeLayer([ makeTestAdapter({ instanceId: CODEX_MODEL_SELECTION.instanceId, driver: CODEX_DRIVER, @@ -504,9 +493,9 @@ describe("orchestration v2 provider switching", () => { }), ]); yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const worker = yield* OrchestrationEffectWorkerV2; - const eventSink = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; + const eventSink = yield* EventSink.EventSinkV2; const screenshot: ChatAttachment = { type: "image", id: "screenshot", @@ -588,7 +577,7 @@ describe("orchestration v2 provider switching", () => { event.payload.delivery?.status === "inline", ) ? Effect.fail( - new EventSinkWriteError({ + new EventSink.EventSinkWriteError({ eventCount: input.events.length, cause: "bookkeeping unavailable", }), @@ -916,7 +905,7 @@ describe("orchestration v2 provider switching", () => { const injectedHistory = yield* Ref.make>([]); const failOnce = yield* Ref.make(true); const generation = yield* Ref.make(0); - const registry = makeProviderAdapterRegistryLayer([ + const registry = ProviderAdapterRegistry.makeLayer([ makeTestAdapter({ instanceId: CODEX_MODEL_SELECTION.instanceId, driver: CODEX_DRIVER, @@ -941,8 +930,8 @@ describe("orchestration v2 provider switching", () => { }), ]); yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const worker = yield* OrchestrationEffectWorkerV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; const dispatch = (ordinal: number, text: string, selection: ModelSelection) => orchestrator.dispatch({ type: "message.dispatch", @@ -1096,7 +1085,7 @@ describe("orchestration v2 provider switching", () => { const originalPrompt = "Keep the release marker violet and preserve the existing API."; const partialResponse = "I checked the API and found the release configuration."; - const registryLayer = makeProviderAdapterRegistryLayer( + const registryLayer = ProviderAdapterRegistry.makeLayer( ( [ [CODEX_MODEL_SELECTION, CODEX_DRIVER, CodexProviderCapabilitiesV2], @@ -1130,8 +1119,8 @@ describe("orchestration v2 provider switching", () => { ), ); yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const worker = yield* OrchestrationEffectWorkerV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; const waitForRun = ( ordinal: number, expectedStatus: "completed" | "failed" | "interrupted", @@ -1278,7 +1267,7 @@ describe("orchestration v2 provider switching", () => { const capturedTurns = yield* Ref.make>([]); const started = yield* Deferred.make(); const scenarioThreadId = ThreadId.make(`thread:queued-capability:${key}`); - const registryLayer = makeProviderAdapterRegistryLayer([ + const registryLayer = ProviderAdapterRegistry.makeLayer([ makeTestAdapter({ instanceId: CODEX_MODEL_SELECTION.instanceId, driver: CODEX_DRIVER, @@ -1310,8 +1299,8 @@ describe("orchestration v2 provider switching", () => { }), ]); yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const worker = yield* OrchestrationEffectWorkerV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; yield* orchestrator.dispatch({ type: "thread.create", createdBy: "user", @@ -1356,7 +1345,7 @@ describe("orchestration v2 provider switching", () => { yield* queue; } else { const error = yield* queue.pipe(Effect.flip); - assert.instanceOf(error, OrchestratorDispatchError); + assert.instanceOf(error, Orchestrator.OrchestratorDispatchError); assert.instanceOf(error.cause, CommandPolicyCapabilityUnsupportedError); assert.equal(error.cause.capability, "queued_messages"); } @@ -1396,7 +1385,7 @@ describe("orchestration v2 provider switching", () => { const cwd = yield* checkpointWorkspace("queued-steer-provider-switch"); const capturedTurns = yield* Ref.make>([]); const started = yield* Deferred.make(); - const registryLayer = makeProviderAdapterRegistryLayer([ + const registryLayer = ProviderAdapterRegistry.makeLayer([ makeTestAdapter({ instanceId: CODEX_MODEL_SELECTION.instanceId, driver: CODEX_DRIVER, @@ -1425,9 +1414,9 @@ describe("orchestration v2 provider switching", () => { ]); const queuedThreadId = ThreadId.make("thread:queued-steer-provider-switch"); const projection = yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const worker = yield* OrchestrationEffectWorkerV2; - const eventSink = yield* EventSinkV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; + const eventSink = yield* EventSink.EventSinkV2; const dispatch = ( key: string, modelSelection: ModelSelection, @@ -1636,19 +1625,23 @@ describe("orchestration v2 provider switching", () => { }), ]; const registryLayer = Layer.succeed( - ProviderAdapterRegistryV2, - ProviderAdapterRegistryV2.of({ + ProviderAdapterRegistry.ProviderAdapterRegistryV2, + ProviderAdapterRegistry.ProviderAdapterRegistryV2.of({ get: (instanceId) => { const adapter = adapters.find((candidate) => candidate.instanceId === instanceId); return adapter === undefined - ? Effect.fail(new ProviderAdapterRegistryLookupError({ instanceId })) + ? Effect.fail( + new ProviderAdapterRegistry.ProviderAdapterRegistryLookupError({ instanceId }), + ) : Effect.succeed(adapter); }, list: () => Effect.succeed(adapters.map((adapter) => adapter.instanceId)), getMetadata: (instanceId) => { const adapter = adapters.find((candidate) => candidate.instanceId === instanceId); return adapter === undefined - ? Effect.fail(new ProviderAdapterRegistryLookupError({ instanceId })) + ? Effect.fail( + new ProviderAdapterRegistry.ProviderAdapterRegistryLookupError({ instanceId }), + ) : Effect.succeed({ driver: CODEX_DRIVER, continuationKey: "codex:shared-native-account-history", @@ -1663,9 +1656,9 @@ describe("orchestration v2 provider switching", () => { ); const queuedThreadId = ThreadId.make("thread:queued-account-switch"); const projection = yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; - const worker = yield* OrchestrationEffectWorkerV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; yield* orchestrator.dispatch({ type: "thread.create", createdBy: "user", @@ -1787,7 +1780,7 @@ describe("orchestration v2 provider switching", () => { const cwd = yield* checkpointWorkspace("queued-provider-switch"); const capturedTurns = yield* Ref.make>([]); const started = yield* Deferred.make(); - const registryLayer = makeProviderAdapterRegistryLayer([ + const registryLayer = ProviderAdapterRegistry.makeLayer([ makeTestAdapter({ instanceId: ProviderInstanceId.make("codex"), driver: CODEX_DRIVER, @@ -1812,12 +1805,12 @@ describe("orchestration v2 provider switching", () => { ]); const queuedThreadId = ThreadId.make("thread:queued-provider-switch"); const databaseLayer = SqlitePersistenceMemory; - const outboxProvided = effectOutboxLayer.pipe(Layer.provide(databaseLayer)); + const outboxProvided = EffectOutbox.layer.pipe(Layer.provide(databaseLayer)); const projection = yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const worker = yield* OrchestrationEffectWorkerV2; - const eventSink = yield* EventSinkV2; - const effectOutbox = yield* EffectOutboxV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; + const eventSink = yield* EventSink.EventSinkV2; + const effectOutbox = yield* EffectOutbox.EffectOutboxV2; const dispatch = (ordinal: number, modelSelection: ModelSelection) => orchestrator.dispatch({ type: "message.dispatch", @@ -2037,7 +2030,7 @@ describe("orchestration v2 provider switching", () => { canConsumeHandoffSummaries: false, }, }; - const registryLayer = makeProviderAdapterRegistryLayer([ + const registryLayer = ProviderAdapterRegistry.makeLayer([ makeTestAdapter({ instanceId: CODEX_MODEL_SELECTION.instanceId, driver: CODEX_DRIVER, @@ -2057,9 +2050,9 @@ describe("orchestration v2 provider switching", () => { }), ]); const projection = yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; - const eventSink = yield* EventSinkV2; - const worker = yield* OrchestrationEffectWorkerV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const eventSink = yield* EventSink.EventSinkV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; yield* orchestrator.dispatch({ type: "thread.create", createdBy: "user", @@ -2245,7 +2238,7 @@ describe("orchestration v2 provider switching", () => { const capturedTurns = yield* Ref.make>([]); const firstTurnStarted = yield* Deferred.make(); const releaseFirstTurn = yield* Deferred.make(); - const registryLayer = makeProviderAdapterRegistryLayer([ + const registryLayer = ProviderAdapterRegistry.makeLayer([ makeTestAdapter({ instanceId: ProviderInstanceId.make("codex"), driver: CODEX_DRIVER, @@ -2266,8 +2259,8 @@ describe("orchestration v2 provider switching", () => { }), ]); const databaseLayer = SqlitePersistenceMemory; - const eventStoreProvided = eventStoreLayer.pipe(Layer.provideMerge(databaseLayer)); - const projectionStoreProvided = projectionStoreLayer.pipe( + const eventStoreProvided = EventStore.layer.pipe(Layer.provideMerge(databaseLayer)); + const projectionStoreProvided = ProjectionStore.layer.pipe( Layer.provideMerge(databaseLayer), ); const storesProvided = Layer.mergeAll( @@ -2275,11 +2268,11 @@ describe("orchestration v2 provider switching", () => { eventStoreProvided, projectionStoreProvided, ); - const eventSinkProvided = eventSinkLayer.pipe(Layer.provide(storesProvided)); - const importerProvided = legacyV1ThreadImporterLayer.pipe( + const eventSinkProvided = EventSink.layer.pipe(Layer.provide(storesProvided)); + const importerProvided = LegacyV1ThreadImporter.layer.pipe( Layer.provide(Layer.mergeAll(storesProvided, eventSinkProvided)), ); - const maintenanceProvided = projectionMaintenanceLayer.pipe(Layer.provide(storesProvided)); + const maintenanceProvided = ProjectionMaintenance.layer.pipe(Layer.provide(storesProvided)); const orchestratorProvided = makeOrchestratorV2ReplayLayerWithRegistry( { name: "provider-switch-legacy-import", @@ -2305,10 +2298,10 @@ describe("orchestration v2 provider switching", () => { const projection = yield* Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - const importer = yield* LegacyV1ThreadImporter; - const maintenance = yield* ProjectionMaintenanceV2; - const orchestrator = yield* OrchestratorV2; - const worker = yield* OrchestrationEffectWorkerV2; + const importer = yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; yield* sql` INSERT INTO projection_projects ( @@ -2531,7 +2524,7 @@ describe("orchestration v2 provider switching", () => { const cwd = yield* checkpointWorkspace("provider-switch"); const capturedTurns = yield* Ref.make>([]); const codexNativeThreadGeneration = yield* Ref.make(0); - const registryLayer = makeProviderAdapterRegistryLayer([ + const registryLayer = ProviderAdapterRegistry.makeLayer([ makeTestAdapter({ instanceId: ProviderInstanceId.make("codex"), driver: CODEX_DRIVER, @@ -2608,7 +2601,7 @@ describe("orchestration v2 provider switching", () => { ] satisfies ReadonlyArray; const projection = yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; yield* orchestrator.dispatch(commands[0]!); yield* orchestrator.dispatch(commands[1]!); yield* waitForIdle(threadId); @@ -2631,7 +2624,7 @@ describe("orchestration v2 provider switching", () => { threadId, providerSessionId: codexSession.id, }); - yield* (yield* OrchestrationEffectWorkerV2).drain(); + yield* (yield* EffectWorker.OrchestrationEffectWorkerV2).drain(); yield* orchestrator.dispatch(commands[3]!); assert.deepEqual( (yield* orchestrator.getThreadProjection(threadId)).thread.modelSelection, @@ -2743,7 +2736,7 @@ describe("orchestration v2 provider switching", () => { const targetPrompt = "What release color did we choose?"; const cwd = yield* checkpointWorkspace("cross-provider-fork"); const capturedTurns = yield* Ref.make>([]); - const registryLayer = makeProviderAdapterRegistryLayer([ + const registryLayer = ProviderAdapterRegistry.makeLayer([ makeTestAdapter({ instanceId: ProviderInstanceId.make("codex"), driver: CODEX_DRIVER, @@ -2813,7 +2806,7 @@ describe("orchestration v2 provider switching", () => { ] satisfies ReadonlyArray; const targetProjection = yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; yield* orchestrator.dispatch(commands[0]!); yield* orchestrator.dispatch(commands[1]!); yield* waitForIdle(sourceThreadId); @@ -2883,7 +2876,7 @@ describe("orchestration v2 provider switching", () => { const targetPrompt = "What deployment marker did we choose?"; const cwd = yield* checkpointWorkspace("cursor-portable-fork"); const capturedTurns = yield* Ref.make>([]); - const registryLayer = makeProviderAdapterRegistryLayer([ + const registryLayer = ProviderAdapterRegistry.makeLayer([ makeTestAdapter({ instanceId: ProviderInstanceId.make("cursor"), driver: CURSOR_DRIVER, @@ -2949,7 +2942,7 @@ describe("orchestration v2 provider switching", () => { ] satisfies ReadonlyArray; const targetProjection = yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; yield* orchestrator.dispatch(commands[0]!); yield* orchestrator.dispatch(commands[1]!); yield* waitForIdle(sourceThreadId); @@ -3019,7 +3012,7 @@ describe("orchestration v2 provider switching", () => { const mergePrompt = "Report all three remembered markers."; const cwd = yield* checkpointWorkspace("cross-provider-merge"); const capturedTurns = yield* Ref.make>([]); - const registryLayer = makeProviderAdapterRegistryLayer([ + const registryLayer = ProviderAdapterRegistry.makeLayer([ makeTestAdapter({ instanceId: ProviderInstanceId.make("codex"), driver: CODEX_DRIVER, @@ -3132,7 +3125,7 @@ describe("orchestration v2 provider switching", () => { ] satisfies ReadonlyArray; const projection = yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; yield* orchestrator.dispatch(commands[0]!); yield* orchestrator.dispatch(commands[1]!); yield* waitForIdle(sourceThreadId); @@ -3149,7 +3142,7 @@ describe("orchestration v2 provider switching", () => { const codexThread = beforeResume.providerThreads.find( (thread) => thread.id === beforeResume.runs[0]?.providerThreadId, )!; - yield* (yield* EventSinkV2).write({ + yield* (yield* EventSink.EventSinkV2).write({ events: [ { id: EventId.make("unloaded-merge-target"), @@ -3244,7 +3237,7 @@ describe("orchestration v2 provider switching", () => { } satisfies ModelSelection; const cwd = yield* checkpointWorkspace("custom-codex-instances"); const capturedTurns = yield* Ref.make>([]); - const registryLayer = makeProviderAdapterRegistryLayer([ + const registryLayer = ProviderAdapterRegistry.makeLayer([ makeTestAdapter({ instanceId: personalSelection.instanceId, driver: CODEX_DRIVER, @@ -3264,7 +3257,7 @@ describe("orchestration v2 provider switching", () => { ]); const [personal, work] = yield* Effect.gen(function* () { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; for (const [targetThreadId, selection, suffix] of [ [personalThreadId, personalSelection, "personal"], [workThreadId, workSelection, "work"], diff --git a/apps/server/src/orchestration-v2/testkit/ThreadFork.integration.test.ts b/apps/server/src/orchestration-v2/testkit/ThreadFork.integration.test.ts index 2d751847a9f3..a2e2c87c3220 100644 --- a/apps/server/src/orchestration-v2/testkit/ThreadFork.integration.test.ts +++ b/apps/server/src/orchestration-v2/testkit/ThreadFork.integration.test.ts @@ -18,7 +18,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import { ClaudeOrchestratorReplayHarness } from "../Adapters/ClaudeAdapterV2.testkit.ts"; import { CodexOrchestratorReplayHarness } from "../Adapters/CodexAdapterV2.testkit.ts"; -import { IdAllocatorV2, layer as idAllocatorLayer } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { provideDeterministicTestRuntime } from "./DeterministicRuntime.ts"; import { THREAD_FORK_NATIVE_PRIOR_TURN_ALPHA_PROMPT, @@ -155,7 +155,7 @@ describe("orchestration V2 thread fork", () => { ); const materialized = yield* Effect.gen(function* () { - const ids = yield* IdAllocatorV2; + const ids = yield* IdAllocator.IdAllocatorV2; const projectId = yield* ids.allocate.project({ fixtureName: "thread-fork-native" }); const sourceThreadId = yield* ids.allocate.thread({ fixtureName: "thread-fork-native-source", @@ -238,7 +238,7 @@ describe("orchestration V2 thread fork", () => { targetThreadId, commands, }; - }).pipe(Effect.provide(idAllocatorLayer), provideDeterministicTestRuntime); + }).pipe(Effect.provide(IdAllocator.layer), provideDeterministicTestRuntime); const result = yield* runOrchestratorV2ProviderReplayScenario( { @@ -331,7 +331,7 @@ describe("orchestration V2 thread fork", () => { ); const materialized = yield* Effect.gen(function* () { - const ids = yield* IdAllocatorV2; + const ids = yield* IdAllocator.IdAllocatorV2; const projectId = yield* ids.allocate.project({ fixtureName: "thread-fork-native" }); const sourceThreadId = yield* ids.allocate.thread({ fixtureName: "thread-fork-native-source", @@ -404,7 +404,7 @@ describe("orchestration V2 thread fork", () => { targetThreadId, commands, }; - }).pipe(Effect.provide(idAllocatorLayer), provideDeterministicTestRuntime); + }).pipe(Effect.provide(IdAllocator.layer), provideDeterministicTestRuntime); const result = yield* runOrchestratorV2ProviderReplayScenario( { @@ -469,7 +469,7 @@ describe("orchestration V2 thread fork", () => { ); const materialized = yield* Effect.gen(function* () { - const ids = yield* IdAllocatorV2; + const ids = yield* IdAllocator.IdAllocatorV2; const projectId = yield* ids.allocate.project({ fixtureName: "thread-fork-native-prior-turn", }); @@ -560,7 +560,7 @@ describe("orchestration V2 thread fork", () => { targetThreadId, commands, }; - }).pipe(Effect.provide(idAllocatorLayer), provideDeterministicTestRuntime); + }).pipe(Effect.provide(IdAllocator.layer), provideDeterministicTestRuntime); const result = yield* runOrchestratorV2ProviderReplayScenario( { @@ -648,7 +648,7 @@ describe("orchestration V2 thread fork", () => { ); const materialized = yield* Effect.gen(function* () { - const ids = yield* IdAllocatorV2; + const ids = yield* IdAllocator.IdAllocatorV2; const projectId = yield* ids.allocate.project({ fixtureName: "thread-fork-native-prior-turn", }); @@ -739,7 +739,7 @@ describe("orchestration V2 thread fork", () => { targetThreadId, commands, }; - }).pipe(Effect.provide(idAllocatorLayer), provideDeterministicTestRuntime); + }).pipe(Effect.provide(IdAllocator.layer), provideDeterministicTestRuntime); const result = yield* runOrchestratorV2ProviderReplayScenario( { @@ -800,7 +800,7 @@ describe("orchestration V2 thread fork", () => { ); const materialized = yield* Effect.gen(function* () { - const ids = yield* IdAllocatorV2; + const ids = yield* IdAllocator.IdAllocatorV2; const projectId = yield* ids.allocate.project({ fixtureName: "thread-fork-native-prior-turn-source-rollback", }); @@ -918,7 +918,7 @@ describe("orchestration V2 thread fork", () => { secondRunId, commands, }; - }).pipe(Effect.provide(idAllocatorLayer), provideDeterministicTestRuntime); + }).pipe(Effect.provide(IdAllocator.layer), provideDeterministicTestRuntime); const result = yield* runOrchestratorV2ProviderReplayScenario( { @@ -1009,7 +1009,7 @@ describe("orchestration V2 thread fork", () => { ); const materialized = yield* Effect.gen(function* () { - const ids = yield* IdAllocatorV2; + const ids = yield* IdAllocator.IdAllocatorV2; const projectId = yield* ids.allocate.project({ fixtureName: "thread-fork-native-fork-local-rollback", }); @@ -1135,7 +1135,7 @@ describe("orchestration V2 thread fork", () => { targetSecondRunId, commands, }; - }).pipe(Effect.provide(idAllocatorLayer), provideDeterministicTestRuntime); + }).pipe(Effect.provide(IdAllocator.layer), provideDeterministicTestRuntime); const result = yield* runOrchestratorV2ProviderReplayScenario( { diff --git a/apps/server/src/orchestration-v2/testkit/ThreadMergeBack.integration.test.ts b/apps/server/src/orchestration-v2/testkit/ThreadMergeBack.integration.test.ts index d7302222e738..92837e4974a7 100644 --- a/apps/server/src/orchestration-v2/testkit/ThreadMergeBack.integration.test.ts +++ b/apps/server/src/orchestration-v2/testkit/ThreadMergeBack.integration.test.ts @@ -15,11 +15,11 @@ import { import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; -import { ProviderAdapterRegistryV2 } from "../ProviderAdapterRegistry.ts"; +import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; import { ClaudeOrchestratorReplayHarness } from "../Adapters/ClaudeAdapterV2.testkit.ts"; import { CodexOrchestratorReplayHarness } from "../Adapters/CodexAdapterV2.testkit.ts"; -import { IdAllocatorV2, layer as idAllocatorLayer } from "../IdAllocator.ts"; +import * as IdAllocator from "../IdAllocator.ts"; import { provideDeterministicTestRuntime } from "./DeterministicRuntime.ts"; import { CODEX_MODEL_SELECTION, @@ -50,9 +50,9 @@ const CodexHistoryReplayHarness: typeof CodexOrchestratorReplayHarness = { ...CodexOrchestratorReplayHarness, makeProviderAdapterRegistryLayer: (transcript, options) => Layer.effect( - ProviderAdapterRegistryV2, + ProviderAdapterRegistry.ProviderAdapterRegistryV2, Effect.gen(function* () { - const registry = yield* ProviderAdapterRegistryV2; + const registry = yield* ProviderAdapterRegistry.ProviderAdapterRegistryV2; return { ...registry, get: (id) => @@ -262,7 +262,7 @@ describe("orchestration V2 merge-back provider replay", () => { Effect.gen(function* () { const rawTranscript = yield* readTranscript("thread_merge_back_continue", variant.driver); const materialized = yield* Effect.gen(function* () { - const ids = yield* IdAllocatorV2; + const ids = yield* IdAllocator.IdAllocatorV2; const projectId = yield* ids.allocate.project({ fixtureName: `thread-merge-back-${variant.driver}`, }); @@ -363,7 +363,7 @@ describe("orchestration V2 merge-back provider replay", () => { }, ] satisfies ReadonlyArray; return { commands, sourceThreadId, forkThreadId, forkRunId }; - }).pipe(Effect.provide(idAllocatorLayer), provideDeterministicTestRuntime); + }).pipe(Effect.provide(IdAllocator.layer), provideDeterministicTestRuntime); const summary = forkDeltaSummary({ sourceThreadId: materialized.forkThreadId, targetThreadId: materialized.sourceThreadId, @@ -458,7 +458,7 @@ describe("orchestration V2 merge-back provider replay", () => { Effect.gen(function* () { const rawTranscript = yield* readTranscript("thread_merge_back_siblings", variant.driver); const materialized = yield* Effect.gen(function* () { - const ids = yield* IdAllocatorV2; + const ids = yield* IdAllocator.IdAllocatorV2; const fixtureName = `thread-merge-back-siblings-${variant.driver}`; const projectId = yield* ids.allocate.project({ fixtureName }); const sourceThreadId = yield* ids.allocate.thread({ @@ -597,7 +597,7 @@ describe("orchestration V2 merge-back provider replay", () => { firstForkRunId, secondForkRunId, }; - }).pipe(Effect.provide(idAllocatorLayer), provideDeterministicTestRuntime); + }).pipe(Effect.provide(IdAllocator.layer), provideDeterministicTestRuntime); const firstSummary = forkDeltaSummary({ sourceThreadId: materialized.firstForkThreadId, targetThreadId: materialized.sourceThreadId, From 6d8deb9e4f7ee478f3eba3b9cba5466c88934385 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 30 Sep 2026 01:35:59 -0700 Subject: [PATCH 4/4] refactor(server): import services as namespaces in orchestration-v2 legacy Replace named tag and layer imports (and layer aliases) with module namespaces in orchestration-v2/legacy. Imports and call sites only. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../LegacyV1Cutover.integration.test.ts | 54 +++++++++---------- .../legacy/LegacyV1ThreadImporter.test.ts | 48 ++++++++--------- .../legacy/LegacyV1ThreadImporter.ts | 11 ++-- 3 files changed, 52 insertions(+), 61 deletions(-) diff --git a/apps/server/src/orchestration-v2/legacy/LegacyV1Cutover.integration.test.ts b/apps/server/src/orchestration-v2/legacy/LegacyV1Cutover.integration.test.ts index 4a97423cc196..7ce2e1fe2542 100644 --- a/apps/server/src/orchestration-v2/legacy/LegacyV1Cutover.integration.test.ts +++ b/apps/server/src/orchestration-v2/legacy/LegacyV1Cutover.integration.test.ts @@ -13,7 +13,7 @@ import { type OrchestrationV2ProviderThread, type OrchestrationV2ThreadProjection, } from "@t3tools/contracts"; -import { layer as nodeSqliteClientLayer } from "@t3tools/shared/nodeSqliteClient"; +import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient"; import * as DateTime from "effect/DateTime"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; @@ -37,25 +37,19 @@ import Migration0047 from "../../persistence/Migrations/047_ProjectionProjectIco import Migration0048 from "../../persistence/Migrations/048_ProjectionThreadBranchPullRequest.ts"; import Migration0049 from "../../persistence/Migrations/049_ProjectionThreadsActiveOrderKey.ts"; import { CodexProviderCapabilitiesV2 } from "../Adapters/CodexAdapterV2.ts"; -import { OrchestrationEffectWorkerV2 } from "../EffectWorker.ts"; -import { layer as eventSinkLayer } from "../EventSink.ts"; -import { layer as eventStoreLayer } from "../EventStore.ts"; -import { - LegacyV1ThreadImporter, - layer as legacyV1ThreadImporterLayer, -} from "./LegacyV1ThreadImporter.ts"; -import { OrchestratorV2 } from "../Orchestrator.ts"; -import { - ProjectionMaintenanceV2, - layer as projectionMaintenanceLayer, -} from "../ProjectionMaintenance.ts"; -import { ProjectionStoreV2, layer as projectionStoreLayer } from "../ProjectionStore.ts"; +import * as EffectWorker from "../EffectWorker.ts"; +import * as EventSink from "../EventSink.ts"; +import * as EventStore from "../EventStore.ts"; +import * as LegacyV1ThreadImporter from "./LegacyV1ThreadImporter.ts"; +import * as Orchestrator from "../Orchestrator.ts"; +import * as ProjectionMaintenance from "../ProjectionMaintenance.ts"; +import * as ProjectionStore from "../ProjectionStore.ts"; import { ProviderAdapterProtocolError, type ProviderAdapterV2Event, type ProviderAdapterV2Shape, } from "../ProviderAdapter.ts"; -import { makeSingleLayer } from "../ProviderAdapterRegistry.ts"; +import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; import { makeOrchestratorV2ReplayLayerWithRegistry } from "../testkit/ProviderReplayHarness.ts"; import { checkpointWorkspace } from "../testkit/ReplayFixtureWorkspace.ts"; @@ -418,7 +412,7 @@ const seedV1Database = (fixturePath: string, workspace: string) => }); yield* sql`PRAGMA wal_checkpoint(TRUNCATE);`; - }).pipe(Effect.provide(nodeSqliteClientLayer({ filename: fixturePath }))), + }).pipe(Effect.provide(NodeSqliteClient.layer({ filename: fixturePath }))), ); interface CapturedTurn { @@ -570,7 +564,7 @@ const makeCodexAdapter = (capturedTurns: Ref.Ref>) = }) satisfies ProviderAdapterV2Shape; const waitForIdle = Effect.fn("LegacyV1Cutover.waitForIdle")(function* (threadId: ThreadId) { - const orchestrator = yield* OrchestratorV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; for (let attempt = 0; attempt < 1_000; attempt += 1) { const projection = yield* orchestrator.getThreadProjection(threadId); if ( @@ -594,14 +588,14 @@ const makeBootLayer = (input: { const databaseLayer = makeSqlitePersistenceLive(input.dbPath).pipe( Layer.provide(NodeServices.layer), ); - const eventStoreProvided = eventStoreLayer.pipe(Layer.provideMerge(databaseLayer)); - const projectionStoreProvided = projectionStoreLayer.pipe(Layer.provideMerge(databaseLayer)); + const eventStoreProvided = EventStore.layer.pipe(Layer.provideMerge(databaseLayer)); + const projectionStoreProvided = ProjectionStore.layer.pipe(Layer.provideMerge(databaseLayer)); const storesProvided = Layer.mergeAll(databaseLayer, eventStoreProvided, projectionStoreProvided); - const eventSinkProvided = eventSinkLayer.pipe(Layer.provide(storesProvided)); - const importerProvided = legacyV1ThreadImporterLayer.pipe( + const eventSinkProvided = EventSink.layer.pipe(Layer.provide(storesProvided)); + const importerProvided = LegacyV1ThreadImporter.layer.pipe( Layer.provide(Layer.mergeAll(storesProvided, eventSinkProvided)), ); - const maintenanceProvided = projectionMaintenanceLayer.pipe(Layer.provide(storesProvided)); + const maintenanceProvided = ProjectionMaintenance.layer.pipe(Layer.provide(storesProvided)); const orchestratorProvided = makeOrchestratorV2ReplayLayerWithRegistry( { name: input.name, @@ -615,7 +609,7 @@ const makeBootLayer = (input: { }, }, }, - makeSingleLayer(makeCodexAdapter(input.capturedTurns)), + ProviderAdapterRegistry.makeSingleLayer(makeCodexAdapter(input.capturedTurns)), { databaseLayer }, ); return Layer.mergeAll( @@ -675,10 +669,10 @@ describe("orchestration v2 legacy v1 cutover", () => { const firstBoot = yield* Effect.scoped( Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - const importer = yield* LegacyV1ThreadImporter; - const maintenance = yield* ProjectionMaintenanceV2; - const projections = yield* ProjectionStoreV2; - const orchestrator = yield* OrchestratorV2; + const importer = yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; + const orchestrator = yield* Orchestrator.OrchestratorV2; assert.equal(yield* importer.pendingThreadCount, ALL_THREADS.length); const shellImport = yield* importer.reconcileShells; @@ -875,7 +869,7 @@ describe("orchestration v2 legacy v1 cutover", () => { // Drain the outbox worker so the persisted event count is settled // before the restart boot re-reads it. - const worker = yield* OrchestrationEffectWorkerV2; + const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; yield* worker.drain(); const migrationEventCount = yield* sql<{ readonly count: number }>` @@ -960,8 +954,8 @@ describe("orchestration v2 legacy v1 cutover", () => { yield* Effect.scoped( Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - const importer = yield* LegacyV1ThreadImporter; - const projections = yield* ProjectionStoreV2; + const importer = yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter; + const projections = yield* ProjectionStore.ProjectionStoreV2; assert.equal(yield* importer.pendingThreadCount, 1); assert.deepStrictEqual(yield* importer.reconcileShells, { diff --git a/apps/server/src/orchestration-v2/legacy/LegacyV1ThreadImporter.test.ts b/apps/server/src/orchestration-v2/legacy/LegacyV1ThreadImporter.test.ts index 894700d231a5..d07a3f5b6ef3 100644 --- a/apps/server/src/orchestration-v2/legacy/LegacyV1ThreadImporter.test.ts +++ b/apps/server/src/orchestration-v2/legacy/LegacyV1ThreadImporter.test.ts @@ -8,27 +8,21 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; import { SqlitePersistenceMemory } from "../../persistence/Layers/Sqlite.ts"; import { listLinkedPullRequestThreads } from "../../pullRequest/linkedThreads.ts"; -import { EventSinkV2, layer as eventSinkLayer } from "../EventSink.ts"; -import { layer as eventStoreLayer } from "../EventStore.ts"; -import { - LegacyV1ThreadImporter, - layer as legacyV1ThreadImporterLayer, -} from "./LegacyV1ThreadImporter.ts"; -import { - ProjectionMaintenanceV2, - layer as projectionMaintenanceLayer, -} from "../ProjectionMaintenance.ts"; -import { ProjectionStoreV2, layer as projectionStoreLayer } from "../ProjectionStore.ts"; +import * as EventSink from "../EventSink.ts"; +import * as EventStore from "../EventStore.ts"; +import * as LegacyV1ThreadImporter from "./LegacyV1ThreadImporter.ts"; +import * as ProjectionMaintenance from "../ProjectionMaintenance.ts"; +import * as ProjectionStore from "../ProjectionStore.ts"; const databaseLayer = SqlitePersistenceMemory; -const eventStoreProvided = eventStoreLayer.pipe(Layer.provideMerge(databaseLayer)); -const projectionStoreProvided = projectionStoreLayer.pipe(Layer.provideMerge(databaseLayer)); +const eventStoreProvided = EventStore.layer.pipe(Layer.provideMerge(databaseLayer)); +const projectionStoreProvided = ProjectionStore.layer.pipe(Layer.provideMerge(databaseLayer)); const storesProvided = Layer.mergeAll(databaseLayer, eventStoreProvided, projectionStoreProvided); -const eventSinkProvided = eventSinkLayer.pipe(Layer.provide(storesProvided)); -const importerProvided = legacyV1ThreadImporterLayer.pipe( +const eventSinkProvided = EventSink.layer.pipe(Layer.provide(storesProvided)); +const importerProvided = LegacyV1ThreadImporter.layer.pipe( Layer.provide(Layer.mergeAll(storesProvided, eventSinkProvided)), ); -const projectionMaintenanceProvided = projectionMaintenanceLayer.pipe( +const projectionMaintenanceProvided = ProjectionMaintenance.layer.pipe( Layer.provide(storesProvided), ); const TestLayer = Layer.mergeAll( @@ -42,7 +36,7 @@ it.layer(TestLayer)("LegacyV1ThreadImporter", (it) => { it.effect("uses the created-thread index for startup migration checks", () => Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - const importer = yield* LegacyV1ThreadImporter; + const importer = yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter; const statements: string[] = []; const tracer = Tracer.make({ span(options) { @@ -84,10 +78,10 @@ it.layer(TestLayer)("LegacyV1ThreadImporter", (it) => { it.effect("imports lightweight shells, hydrates transcripts, and remains idempotent", () => Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - const importer = yield* LegacyV1ThreadImporter; - const maintenance = yield* ProjectionMaintenanceV2; - const projections = yield* ProjectionStoreV2; - const eventSink = yield* EventSinkV2; + const importer = yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; const threadId = ThreadId.make("thread:legacy-import"); yield* sql` @@ -420,10 +414,10 @@ it.layer(TestLayer)("LegacyV1ThreadImporter", (it) => { it.effect("repairs newly added metadata after an earlier metadata repair", () => Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; - const importer = yield* LegacyV1ThreadImporter; - const maintenance = yield* ProjectionMaintenanceV2; - const projections = yield* ProjectionStoreV2; - const eventSink = yield* EventSinkV2; + const importer = yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter; + const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; + const projections = yield* ProjectionStore.ProjectionStoreV2; + const eventSink = yield* EventSink.EventSinkV2; const threadId = ThreadId.make("thread:legacy-metadata-upgrade"); const previousRepairId = EventId.make(`migration:v1:thread:${threadId}:metadata-repair`); @@ -521,9 +515,9 @@ it.layer(TestLayer)("LegacyV1ThreadImporter", (it) => { assert.deepStrictEqual(replayed.thread, repaired.thread); assert.deepStrictEqual( yield* Effect.gen(function* () { - const restartedImporter = yield* LegacyV1ThreadImporter; + const restartedImporter = yield* LegacyV1ThreadImporter.LegacyV1ThreadImporter; return yield* restartedImporter.reconcileShells; - }).pipe(Effect.provide(legacyV1ThreadImporterLayer)), + }).pipe(Effect.provide(LegacyV1ThreadImporter.layer)), { importedThreadCount: 0, importedMessageCount: 0 }, ); const eventsAfterRestart = yield* sql<{ readonly event_id: string }>` diff --git a/apps/server/src/orchestration-v2/legacy/LegacyV1ThreadImporter.ts b/apps/server/src/orchestration-v2/legacy/LegacyV1ThreadImporter.ts index 29c6535f3ad0..20988ac44f00 100644 --- a/apps/server/src/orchestration-v2/legacy/LegacyV1ThreadImporter.ts +++ b/apps/server/src/orchestration-v2/legacy/LegacyV1ThreadImporter.ts @@ -29,7 +29,7 @@ import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; import * as SqlClient from "effect/unstable/sql/SqlClient"; -import { EventSinkV2 } from "../EventSink.ts"; +import * as EventSink from "../EventSink.ts"; import { makeKeyedSerialExecutor } from "../KeyedSerialExecutor.ts"; import { randomUuidV4 } from "../RandomUuid.ts"; @@ -346,7 +346,7 @@ function chunks(items: ReadonlyArray, size: number): Array(); const listMessages = (threadId: ThreadId) => @@ -826,5 +826,8 @@ const make = Effect.gen(function* () { }); }); -export const layer: Layer.Layer = - Layer.effect(LegacyV1ThreadImporter, make); +export const layer: Layer.Layer< + LegacyV1ThreadImporter, + never, + EventSink.EventSinkV2 | SqlClient.SqlClient +> = Layer.effect(LegacyV1ThreadImporter, make);