Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5f81c3a
Refactor provider Effect services
juliusmarminge Jun 20, 2026
84207d0
Tighten provider Effect service refactor
juliusmarminge Jun 20, 2026
3c86d90
Use direct Effect service namespace imports
juliusmarminge Jun 20, 2026
48ad002
Tighten provider refactor tests and predicates
juliusmarminge Jun 20, 2026
f8eb67d
Namespace OpenCode runtime imports
juliusmarminge Jun 20, 2026
7f05bcd
fix: preserve provider failure structure
juliusmarminge Jun 20, 2026
71c6069
Use tagged catch for command resolution
juliusmarminge Jun 20, 2026
7c257da
Preserve hydrated provider cache during boot
juliusmarminge Jun 20, 2026
c9f4596
Distinguish initial provider snapshots from live probes
juliusmarminge Jun 20, 2026
d267246
fix: bound OpenCode failure diagnostics
juliusmarminge Jun 20, 2026
65ff046
fix(provider): sanitize OpenCode endpoint messages
juliusmarminge Jun 20, 2026
6524964
fix(provider): preserve IPv6 display targets
juliusmarminge Jun 20, 2026
4934dc2
test(server): remove cause-only provider assertion
juliusmarminge Jun 20, 2026
d59b591
Classify missing provider bindings
juliusmarminge Jun 20, 2026
c56fe69
Remove provider error constructor indirection
juliusmarminge Jun 20, 2026
44a94f4
Cover whitespace-only provider turn validation
juliusmarminge Jun 20, 2026
6830618
Structure OpenCode probe failures
juliusmarminge Jun 21, 2026
2c81a9b
Preserve provider update error causes
juliusmarminge Jun 21, 2026
2e124c9
Reject whitespace-only provider turns
juliusmarminge Jun 21, 2026
60a235b
Structure provider adapter failure context
juliusmarminge Jun 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 15 additions & 16 deletions apps/server/integration/OrchestrationEngineHarness.integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,13 @@ import { makeSqlitePersistenceLive } from "../src/persistence/Layers/Sqlite.ts";
import { ProjectionCheckpointRepository } from "../src/persistence/Services/ProjectionCheckpoints.ts";
import { ProjectionPendingApprovalRepository } from "../src/persistence/Services/ProjectionPendingApprovals.ts";
import { makeAdapterRegistryMock } from "../src/provider/testUtils/providerAdapterRegistryMock.ts";
import { ProviderAdapterRegistry } from "../src/provider/Services/ProviderAdapterRegistry.ts";
import * as ProviderAdapterRegistry from "../src/provider/ProviderAdapterRegistry.ts";
import { makeProviderRegistryLayer } from "../src/provider/testUtils/providerRegistryMock.ts";
import { ProviderSessionDirectoryLive } from "../src/provider/Layers/ProviderSessionDirectory.ts";
import * as ProviderSessionDirectory from "../src/provider/ProviderSessionDirectory.ts";
import { ServerSettingsService } from "../src/serverSettings.ts";
import { makeProviderServiceLive } from "../src/provider/Layers/ProviderService.ts";
import * as ProviderService from "../src/provider/ProviderService.ts";
import { makeCodexAdapter } from "../src/provider/Layers/CodexAdapter.ts";
import {
NoOpProviderEventLoggers,
ProviderEventLoggers,
} from "../src/provider/Layers/ProviderEventLoggers.ts";
import { ProviderService } from "../src/provider/Services/ProviderService.ts";
import * as ProviderEventLoggers from "../src/provider/ProviderEventLoggers.ts";
import { AnalyticsService } from "../src/telemetry/Services/AnalyticsService.ts";
import { CheckpointReactorLive } from "../src/orchestration/Layers/CheckpointReactor.ts";
import * as RepositoryIdentityResolver from "../src/project/RepositoryIdentityResolver.ts";
Expand Down Expand Up @@ -178,7 +174,7 @@ export interface OrchestrationIntegrationHarness {
readonly adapterHarness: TestProviderAdapterHarness | null;
readonly engine: OrchestrationEngineShape;
readonly snapshotQuery: ProjectionSnapshotQuery["Service"];
readonly providerService: ProviderService["Service"];
readonly providerService: ProviderService.ProviderService["Service"];
readonly checkpointStore: CheckpointStore.CheckpointStore["Service"];
readonly checkpointRepository: ProjectionCheckpointRepository["Service"];
readonly pendingApprovalRepository: ProjectionPendingApprovalRepository["Service"];
Expand Down Expand Up @@ -241,7 +237,7 @@ export const makeOrchestrationIntegrationHarness = (
});
const fakeRegistry = adapterHarness
? Layer.succeed(
ProviderAdapterRegistry,
ProviderAdapterRegistry.ProviderAdapterRegistry,
makeAdapterRegistryMock({ [adapterHarness.provider]: adapterHarness.adapter }),
)
: null;
Expand All @@ -262,11 +258,11 @@ export const makeOrchestrationIntegrationHarness = (
Layer.provide(OrchestrationEventStoreLive),
Layer.provide(OrchestrationCommandReceiptRepositoryLive),
);
const providerSessionDirectoryLayer = ProviderSessionDirectoryLive.pipe(
const providerSessionDirectoryLayer = ProviderSessionDirectory.layer.pipe(
Layer.provide(ProviderSessionRuntimeRepositoryLive),
);
const realCodexRegistry = Layer.effect(
ProviderAdapterRegistry,
ProviderAdapterRegistry.ProviderAdapterRegistry,
Effect.gen(function* () {
const codexSettings = yield* decodeCodexSettings({});
const codexAdapter = yield* makeCodexAdapter(codexSettings);
Expand All @@ -279,15 +275,18 @@ export const makeOrchestrationIntegrationHarness = (
Layer.provideMerge(NodeServices.layer),
Layer.provideMerge(providerSessionDirectoryLayer),
);
const providerEventLoggersLayer = Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers);
const providerEventLoggersLayer = Layer.succeed(
ProviderEventLoggers.ProviderEventLoggers,
ProviderEventLoggers.NoOpProviderEventLoggers,
);
const providerLayer = useRealCodex
? makeProviderServiceLive().pipe(
? ProviderService.layer.pipe(
Layer.provide(providerSessionDirectoryLayer),
Layer.provide(realCodexRegistry),
Layer.provide(AnalyticsService.layerTest),
Layer.provide(providerEventLoggersLayer),
)
: makeProviderServiceLive().pipe(
: ProviderService.layer.pipe(
Layer.provide(providerSessionDirectoryLayer),
Layer.provide(fakeRegistry!),
Layer.provide(AnalyticsService.layerTest),
Expand Down Expand Up @@ -395,7 +394,7 @@ export const makeOrchestrationIntegrationHarness = (
runtime.runPromise(Effect.service(ProjectionSnapshotQuery)),
).pipe(Effect.orDie);
const providerService = yield* tryRuntimePromise("load ProviderService service", () =>
runtime.runPromise(Effect.service(ProviderService)),
runtime.runPromise(Effect.service(ProviderService.ProviderService)),
).pipe(Effect.orDie);
const checkpointStore = yield* tryRuntimePromise("load CheckpointStore service", () =>
runtime.runPromise(Effect.service(CheckpointStore.CheckpointStore)),
Expand Down
24 changes: 12 additions & 12 deletions apps/server/integration/TestProviderAdapter.integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,21 +206,16 @@ function nowIso(): string {
return "2026-01-01T00:00:00.000Z";
}

function sessionNotFound(
provider: ProviderDriverKind,
threadId: ThreadId,
): ProviderAdapterSessionNotFoundError {
return new ProviderAdapterSessionNotFoundError({
provider,
threadId: String(threadId),
});
}

function missingSessionEffect(
provider: ProviderDriverKind,
threadId: ThreadId,
): Effect.Effect<never, ProviderAdapterError> {
return Effect.fail(sessionNotFound(provider, threadId));
return Effect.fail(
new ProviderAdapterSessionNotFoundError({
provider,
threadId: String(threadId),
}),
);
}

export const makeTestProviderAdapterHarness = (options?: MakeTestProviderAdapterHarnessOptions) =>
Expand Down Expand Up @@ -517,7 +512,12 @@ export const makeTestProviderAdapterHarness = (options?: MakeTestProviderAdapter
? Effect.sync(() => {
state.queuedResponses.push(response);
})
: Effect.fail(sessionNotFound(provider, threadId)),
: Effect.fail(
new ProviderAdapterSessionNotFoundError({
provider,
threadId: String(threadId),
}),
),
),
);

Expand Down
44 changes: 20 additions & 24 deletions apps/server/integration/providerService.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,13 @@ import * as Path from "effect/Path";
import * as Queue from "effect/Queue";
import * as Stream from "effect/Stream";

import { ProviderAdapterRegistry } from "../src/provider/Services/ProviderAdapterRegistry.ts";
import * as ProviderAdapterRegistry from "../src/provider/ProviderAdapterRegistry.ts";
import { makeAdapterRegistryMock } from "../src/provider/testUtils/providerAdapterRegistryMock.ts";
import { ProviderSessionDirectoryLive } from "../src/provider/Layers/ProviderSessionDirectory.ts";
import {
NoOpProviderEventLoggers,
ProviderEventLoggers,
} from "../src/provider/Layers/ProviderEventLoggers.ts";
import { makeProviderServiceLive } from "../src/provider/Layers/ProviderService.ts";
import {
ProviderService,
type ProviderServiceShape,
} from "../src/provider/Services/ProviderService.ts";
import { ServerSettingsService } from "../src/serverSettings.ts";
import { AnalyticsService } from "../src/telemetry/Services/AnalyticsService.ts";
import * as ProviderSessionDirectory from "../src/provider/ProviderSessionDirectory.ts";
import * as ProviderEventLoggers from "../src/provider/ProviderEventLoggers.ts";
import * as ProviderService from "../src/provider/ProviderService.ts";
import * as ServerSettings from "../src/serverSettings.ts";
import * as AnalyticsService from "../src/telemetry/AnalyticsService.ts";
import { SqlitePersistenceMemory } from "../src/persistence/Layers/Sqlite.ts";
import * as ProviderSessionRuntime from "../src/persistence/ProviderSessionRuntime.ts";

Expand Down Expand Up @@ -51,7 +44,7 @@ const makeWorkspaceDirectory = Effect.gen(function* () {
interface IntegrationFixture {
readonly cwd: string;
readonly harness: TestProviderAdapterHarness;
readonly layer: Layer.Layer<ProviderService, unknown, never>;
readonly layer: Layer.Layer<ProviderService.ProviderService, unknown, never>;
}

const makeIntegrationFixture = Effect.gen(function* () {
Expand All @@ -62,19 +55,22 @@ const makeIntegrationFixture = Effect.gen(function* () {
[ProviderDriverKind.make("codex")]: harness.adapter,
});

const directoryLayer = ProviderSessionDirectoryLive.pipe(
const directoryLayer = ProviderSessionDirectory.layer.pipe(
Layer.provide(ProviderSessionRuntime.layer),
);

const shared = Layer.mergeAll(
directoryLayer,
Layer.succeed(ProviderAdapterRegistry, registry),
ServerSettingsService.layerTest(DEFAULT_SERVER_SETTINGS),
Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, registry),
ServerSettings.ServerSettingsService.layerTest(DEFAULT_SERVER_SETTINGS),
AnalyticsService.layerTest,
Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers),
Layer.succeed(
ProviderEventLoggers.ProviderEventLoggers,
ProviderEventLoggers.NoOpProviderEventLoggers,
),
).pipe(Layer.provide(SqlitePersistenceMemory));

const layer = makeProviderServiceLive().pipe(Layer.provide(shared));
const layer = ProviderService.layer.pipe(Layer.provide(shared));

return {
cwd,
Expand Down Expand Up @@ -105,7 +101,7 @@ const collectEventsDuring = <A, E, R>(
});

const runTurn = (input: {
readonly provider: ProviderServiceShape;
readonly provider: ProviderService.ProviderService["Service"];
readonly harness: TestProviderAdapterHarness;
readonly threadId: ThreadId;
readonly userText: string;
Expand All @@ -129,7 +125,7 @@ it.live("replays typed runtime fixture events", () =>
const fixture = yield* makeIntegrationFixture;

yield* Effect.gen(function* () {
const provider = yield* ProviderService;
const provider = yield* ProviderService.ProviderService;
const session = yield* provider.startSession(ThreadId.make("thread-integration-typed"), {
threadId: ThreadId.make("thread-integration-typed"),
provider: ProviderDriverKind.make("codex"),
Expand Down Expand Up @@ -166,7 +162,7 @@ it.live("replays file-changing fixture turn events", () =>
const { writeFileString } = yield* FileSystem.FileSystem;

yield* Effect.gen(function* () {
const provider = yield* ProviderService;
const provider = yield* ProviderService.ProviderService;
const session = yield* provider.startSession(ThreadId.make("thread-integration-tools"), {
threadId: ThreadId.make("thread-integration-tools"),
provider: ProviderDriverKind.make("codex"),
Expand Down Expand Up @@ -203,7 +199,7 @@ it.live("runs multi-turn tool/approval flow", () =>
const { writeFileString } = yield* FileSystem.FileSystem;

yield* Effect.gen(function* () {
const provider = yield* ProviderService;
const provider = yield* ProviderService.ProviderService;
const session = yield* provider.startSession(ThreadId.make("thread-integration-multi"), {
threadId: ThreadId.make("thread-integration-multi"),
provider: ProviderDriverKind.make("codex"),
Expand Down Expand Up @@ -255,7 +251,7 @@ it.live("rolls back provider conversation state only", () =>
const { writeFileString, readFileString } = yield* FileSystem.FileSystem;

yield* Effect.gen(function* () {
const provider = yield* ProviderService;
const provider = yield* ProviderService.ProviderService;
const session = yield* provider.startSession(ThreadId.make("thread-integration-rollback"), {
threadId: ThreadId.make("thread-integration-rollback"),
provider: ProviderDriverKind.make("codex"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,7 @@ import {
} from "../Services/OrchestrationEngine.ts";
import { CheckpointReactor } from "../Services/CheckpointReactor.ts";
import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts";
import {
ProviderService,
type ProviderServiceShape,
} from "../../provider/Services/ProviderService.ts";
import * as ProviderService from "../../provider/ProviderService.ts";
import { checkpointRefForThreadTurn } from "../../checkpointing/Utils.ts";
import { ServerConfig } from "../../config.ts";
import * as WorkspaceEntries from "../../workspace/WorkspaceEntries.ts";
Expand Down Expand Up @@ -102,7 +99,7 @@ function createProviderServiceHarness(
},
] satisfies ReadonlyArray<ProviderSession>)
: Effect.succeed([] as ReadonlyArray<ProviderSession>);
const service: ProviderServiceShape = {
const service: ProviderService.ProviderService["Service"] = {
startSession: () => unsupported(),
sendTurn: () => unsupported(),
interruptTurn: () => unsupported(),
Expand Down Expand Up @@ -328,7 +325,7 @@ describe("CheckpointReactor", () => {
Layer.provideMerge(orchestrationLayer),
Layer.provideMerge(projectionSnapshotLayer),
Layer.provideMerge(RuntimeReceiptBusLive),
Layer.provideMerge(Layer.succeed(ProviderService, provider.service)),
Layer.provideMerge(Layer.succeed(ProviderService.ProviderService, provider.service)),
Layer.provideMerge(vcsStatusBroadcasterLayer),
Layer.provideMerge(CheckpointStore.layer.pipe(Layer.provide(VcsDriverRegistry.layer))),
Layer.provideMerge(
Expand Down
4 changes: 2 additions & 2 deletions apps/server/src/orchestration/Layers/CheckpointReactor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
resolveThreadWorkspaceCwd,
} from "../../checkpointing/Utils.ts";
import * as CheckpointStore from "../../checkpointing/CheckpointStore.ts";
import { ProviderService } from "../../provider/Services/ProviderService.ts";
import * as ProviderService from "../../provider/ProviderService.ts";
import { CheckpointReactor, type CheckpointReactorShape } from "../Services/CheckpointReactor.ts";
import { OrchestrationEngineService } from "../Services/OrchestrationEngine.ts";
import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts";
Expand Down Expand Up @@ -80,7 +80,7 @@ const make = Effect.gen(function* () {
randomUUID.pipe(Effect.map((uuid) => CommandId.make(`server:${tag}:${uuid}`)));
const orchestrationEngine = yield* OrchestrationEngineService;
const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
const providerService = yield* ProviderService;
const providerService = yield* ProviderService.ProviderService;
const checkpointStore = yield* CheckpointStore.CheckpointStore;
const receiptBus = yield* RuntimeReceiptBus;
const workspaceEntries = yield* WorkspaceEntries.WorkspaceEntries;
Expand Down
31 changes: 17 additions & 14 deletions apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ import { ProviderAdapterRequestError } from "../../provider/Errors.ts";
import { OrchestrationEventStoreLive } from "../../persistence/Layers/OrchestrationEventStore.ts";
import { OrchestrationCommandReceiptRepositoryLive } from "../../persistence/Layers/OrchestrationCommandReceipts.ts";
import { SqlitePersistenceMemory } from "../../persistence/Layers/Sqlite.ts";
import {
ProviderService,
type ProviderServiceShape,
} from "../../provider/Services/ProviderService.ts";
import * as ProviderService from "../../provider/ProviderService.ts";
import { makeProviderRegistryLayer } from "../../provider/testUtils/providerRegistryMock.ts";
import { TextGeneration, type TextGenerationShape } from "../../textGeneration/TextGeneration.ts";
import * as RepositoryIdentityResolver from "../../project/RepositoryIdentityResolver.ts";
Expand Down Expand Up @@ -222,8 +219,12 @@ describe("ProviderCommandReactor", () => {
}),
);
const interruptTurn = vi.fn((_: unknown) => Effect.void);
const respondToRequest = vi.fn<ProviderServiceShape["respondToRequest"]>(() => Effect.void);
const respondToUserInput = vi.fn<ProviderServiceShape["respondToUserInput"]>(() => Effect.void);
const respondToRequest = vi.fn<ProviderService.ProviderService["Service"]["respondToRequest"]>(
() => Effect.void,
);
const respondToUserInput = vi.fn<
ProviderService.ProviderService["Service"]["respondToUserInput"]
>(() => Effect.void);
const stopSession = vi.fn((input: unknown) =>
Effect.sync(() => {
const threadId =
Expand Down Expand Up @@ -294,13 +295,15 @@ describe("ProviderCommandReactor", () => {
];

const unsupported = () => Effect.die(new Error("Unsupported provider call in test")) as never;
const service: ProviderServiceShape = {
startSession: startSession as ProviderServiceShape["startSession"],
sendTurn: sendTurn as ProviderServiceShape["sendTurn"],
interruptTurn: interruptTurn as ProviderServiceShape["interruptTurn"],
respondToRequest: respondToRequest as ProviderServiceShape["respondToRequest"],
respondToUserInput: respondToUserInput as ProviderServiceShape["respondToUserInput"],
stopSession: stopSession as ProviderServiceShape["stopSession"],
const service: ProviderService.ProviderService["Service"] = {
startSession: startSession as ProviderService.ProviderService["Service"]["startSession"],
sendTurn: sendTurn as ProviderService.ProviderService["Service"]["sendTurn"],
interruptTurn: interruptTurn as ProviderService.ProviderService["Service"]["interruptTurn"],
respondToRequest:
respondToRequest as ProviderService.ProviderService["Service"]["respondToRequest"],
respondToUserInput:
respondToUserInput as ProviderService.ProviderService["Service"]["respondToUserInput"],
stopSession: stopSession as ProviderService.ProviderService["Service"]["stopSession"],
listSessions: () => Effect.succeed(runtimeSessions),
getCapabilities: (_provider) =>
Effect.succeed({
Expand Down Expand Up @@ -346,7 +349,7 @@ describe("ProviderCommandReactor", () => {
const layer = ProviderCommandReactorLive.pipe(
Layer.provideMerge(orchestrationLayer),
Layer.provideMerge(projectionSnapshotLayer),
Layer.provideMerge(Layer.succeed(ProviderService, service)),
Layer.provideMerge(Layer.succeed(ProviderService.ProviderService, service)),
Layer.provideMerge(makeProviderRegistryLayer(providerSnapshots as never)),
Layer.provideMerge(
Layer.mock(GitWorkflowService.GitWorkflowService)({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ import { increment, orchestrationEventsProcessedTotal } from "../../observabilit
import { ProviderAdapterRequestError } from "../../provider/Errors.ts";
import type { ProviderServiceError } from "../../provider/Errors.ts";
import { TextGeneration } from "../../textGeneration/TextGeneration.ts";
import { ProviderService } from "../../provider/Services/ProviderService.ts";
import { ProviderRegistry } from "../../provider/Services/ProviderRegistry.ts";
import * as ProviderService from "../../provider/ProviderService.ts";
import * as ProviderRegistry from "../../provider/ProviderRegistry.ts";
import { OrchestrationEngineService } from "../Services/OrchestrationEngine.ts";
import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts";
import {
Expand Down Expand Up @@ -190,8 +190,8 @@ const make = Effect.gen(function* () {
const crypto = yield* Crypto.Crypto;
const orchestrationEngine = yield* OrchestrationEngineService;
const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
const providerService = yield* ProviderService;
const providerRegistry = yield* ProviderRegistry;
const providerService = yield* ProviderService.ProviderService;
const providerRegistry = yield* ProviderRegistry.ProviderRegistry;
const gitWorkflow = yield* GitWorkflowService;
const vcsStatusBroadcaster = yield* VcsStatusBroadcaster;
const textGeneration = yield* TextGeneration;
Expand Down
Loading
Loading