Unified · Split
| @@ -33,17 +33,13 @@ import { makeSqlitePersistenceLive } from "../src/persistence/Layers/Sqlite.ts"; · 32 unmodified lines | |||
| 33 | import { ProjectionCheckpointRepository } from "../src/persistence/Services/ProjectionCheckpoints.ts"; | 33 | import { ProjectionCheckpointRepository } from "../src/persistence/Services/ProjectionCheckpoints.ts"; |
| 34 | import { ProjectionPendingApprovalRepository } from "../src/persistence/Services/ProjectionPendingApprovals.ts"; | 34 | import { ProjectionPendingApprovalRepository } from "../src/persistence/Services/ProjectionPendingApprovals.ts"; |
| 35 | import { makeAdapterRegistryMock } from "../src/provider/testUtils/providerAdapterRegistryMock.ts"; | 35 | import { makeAdapterRegistryMock } from "../src/provider/testUtils/providerAdapterRegistryMock.ts"; |
| 36 | import { ProviderAdapterRegistry } from "../src/provider/Services/ProviderAdapterRegistry.ts"; | 36 | import * as ProviderAdapterRegistry from "../src/provider/ProviderAdapterRegistry.ts"; |
| 37 | import { makeProviderRegistryLayer } from "../src/provider/testUtils/providerRegistryMock.ts"; | 37 | import { makeProviderRegistryLayer } from "../src/provider/testUtils/providerRegistryMock.ts"; |
| 38 | import { ProviderSessionDirectoryLive } from "../src/provider/Layers/ProviderSessionDirectory.ts"; | 38 | import * as ProviderSessionDirectory from "../src/provider/ProviderSessionDirectory.ts"; |
| 39 | import { ServerSettingsService } from "../src/serverSettings.ts"; | 39 | import { ServerSettingsService } from "../src/serverSettings.ts"; |
| 40 | import { makeProviderServiceLive } from "../src/provider/Layers/ProviderService.ts"; | 40 | import * as ProviderService from "../src/provider/ProviderService.ts"; |
| 41 | import { makeCodexAdapter } from "../src/provider/Layers/CodexAdapter.ts"; | 41 | import { makeCodexAdapter } from "../src/provider/Layers/CodexAdapter.ts"; |
| 42 | import { | 42 | import * as ProviderEventLoggers from "../src/provider/ProviderEventLoggers.ts"; |
| 43 | NoOpProviderEventLoggers, | ||
| 44 | ProviderEventLoggers, | ||
| 45 | } from "../src/provider/Layers/ProviderEventLoggers.ts"; | ||
| 46 | import { ProviderService } from "../src/provider/Services/ProviderService.ts"; | ||
| 47 | import { AnalyticsService } from "../src/telemetry/Services/AnalyticsService.ts"; | 43 | import { AnalyticsService } from "../src/telemetry/Services/AnalyticsService.ts"; |
| 48 | import { CheckpointReactorLive } from "../src/orchestration/Layers/CheckpointReactor.ts"; | 44 | import { CheckpointReactorLive } from "../src/orchestration/Layers/CheckpointReactor.ts"; |
| 49 | import * as RepositoryIdentityResolver from "../src/project/RepositoryIdentityResolver.ts"; | 45 | import * as RepositoryIdentityResolver from "../src/project/RepositoryIdentityResolver.ts"; |
| @@ -178,7 +174,7 @@ export interface OrchestrationIntegrationHarness { · 128 unmodified lines | |||
| 178 | readonly adapterHarness: TestProviderAdapterHarness | null; | 174 | readonly adapterHarness: TestProviderAdapterHarness | null; |
| 179 | readonly engine: OrchestrationEngineShape; | 175 | readonly engine: OrchestrationEngineShape; |
| 180 | readonly snapshotQuery: ProjectionSnapshotQuery["Service"]; | 176 | readonly snapshotQuery: ProjectionSnapshotQuery["Service"]; |
| 181 | readonly providerService: ProviderService["Service"]; | 177 | readonly providerService: ProviderService.ProviderService["Service"]; |
| 182 | readonly checkpointStore: CheckpointStore.CheckpointStore["Service"]; | 178 | readonly checkpointStore: CheckpointStore.CheckpointStore["Service"]; |
| 183 | readonly checkpointRepository: ProjectionCheckpointRepository["Service"]; | 179 | readonly checkpointRepository: ProjectionCheckpointRepository["Service"]; |
| 184 | readonly pendingApprovalRepository: ProjectionPendingApprovalRepository["Service"]; | 180 | readonly pendingApprovalRepository: ProjectionPendingApprovalRepository["Service"]; |
| @@ -241,7 +237,7 @@ export const makeOrchestrationIntegrationHarness = ( · 56 unmodified lines | |||
| 241 | }); | 237 | }); |
| 242 | const fakeRegistry = adapterHarness | 238 | const fakeRegistry = adapterHarness |
| 243 | ? Layer.succeed( | 239 | ? Layer.succeed( |
| 244 | ProviderAdapterRegistry, | 240 | ProviderAdapterRegistry.ProviderAdapterRegistry, |
| 245 | makeAdapterRegistryMock({ [adapterHarness.provider]: adapterHarness.adapter }), | 241 | makeAdapterRegistryMock({ [adapterHarness.provider]: adapterHarness.adapter }), |
| 246 | ) | 242 | ) |
| 247 | : null; | 243 | : null; |
| @@ -262,11 +258,11 @@ export const makeOrchestrationIntegrationHarness = ( · 14 unmodified lines | |||
| 262 | Layer.provide(OrchestrationEventStoreLive), | 258 | Layer.provide(OrchestrationEventStoreLive), |
| 263 | Layer.provide(OrchestrationCommandReceiptRepositoryLive), | 259 | Layer.provide(OrchestrationCommandReceiptRepositoryLive), |
| 264 | ); | 260 | ); |
| 265 | const providerSessionDirectoryLayer = ProviderSessionDirectoryLive.pipe( | 261 | const providerSessionDirectoryLayer = ProviderSessionDirectory.layer.pipe( |
| 266 | Layer.provide(ProviderSessionRuntimeRepositoryLive), | 262 | Layer.provide(ProviderSessionRuntimeRepositoryLive), |
| 267 | ); | 263 | ); |
| 268 | const realCodexRegistry = Layer.effect( | 264 | const realCodexRegistry = Layer.effect( |
| 269 | ProviderAdapterRegistry, | 265 | ProviderAdapterRegistry.ProviderAdapterRegistry, |
| 270 | Effect.gen(function* () { | 266 | Effect.gen(function* () { |
| 271 | const codexSettings = yield* decodeCodexSettings({}); | 267 | const codexSettings = yield* decodeCodexSettings({}); |
| 272 | const codexAdapter = yield* makeCodexAdapter(codexSettings); | 268 | const codexAdapter = yield* makeCodexAdapter(codexSettings); |
| @@ -279,15 +275,18 @@ export const makeOrchestrationIntegrationHarness = ( · 6 unmodified lines | |||
| 279 | Layer.provideMerge(NodeServices.layer), | 275 | Layer.provideMerge(NodeServices.layer), |
| 280 | Layer.provideMerge(providerSessionDirectoryLayer), | 276 | Layer.provideMerge(providerSessionDirectoryLayer), |
| 281 | ); | 277 | ); |
| 282 | const providerEventLoggersLayer = Layer.succeed(ProviderEventLoggers, NoOpProviderEventLoggers); | 278 | const providerEventLoggersLayer = Layer.succeed( |
| 279 | ProviderEventLoggers.ProviderEventLoggers, | ||
| 280 | ProviderEventLoggers.NoOpProviderEventLoggers, | ||
| 281 | ); | ||
| 283 | const providerLayer = useRealCodex | 282 | const providerLayer = useRealCodex |
| 284 | ? makeProviderServiceLive().pipe( | 283 | ? ProviderService.layer.pipe( |
| 285 | Layer.provide(providerSessionDirectoryLayer), | 284 | Layer.provide(providerSessionDirectoryLayer), |
| 286 | Layer.provide(realCodexRegistry), | 285 | Layer.provide(realCodexRegistry), |
| 287 | Layer.provide(AnalyticsService.layerTest), | 286 | Layer.provide(AnalyticsService.layerTest), |
| 288 | Layer.provide(providerEventLoggersLayer), | 287 | Layer.provide(providerEventLoggersLayer), |
| 289 | ) | 288 | ) |
| 290 | : makeProviderServiceLive().pipe( | 289 | : ProviderService.layer.pipe( |
| 291 | Layer.provide(providerSessionDirectoryLayer), | 290 | Layer.provide(providerSessionDirectoryLayer), |
| 292 | Layer.provide(fakeRegistry!), | 291 | Layer.provide(fakeRegistry!), |
| 293 | Layer.provide(AnalyticsService.layerTest), | 292 | Layer.provide(AnalyticsService.layerTest), |
| @@ -395,7 +394,7 @@ export const makeOrchestrationIntegrationHarness = ( · 101 unmodified lines | |||
| 395 | runtime.runPromise(Effect.service(ProjectionSnapshotQuery)), | 394 | runtime.runPromise(Effect.service(ProjectionSnapshotQuery)), |
| 396 | ).pipe(Effect.orDie); | 395 | ).pipe(Effect.orDie); |
| 397 | const providerService = yield* tryRuntimePromise("load ProviderService service", () => | 396 | const providerService = yield* tryRuntimePromise("load ProviderService service", () => |
| 398 | runtime.runPromise(Effect.service(ProviderService)), | 397 | runtime.runPromise(Effect.service(ProviderService.ProviderService)), |
| 399 | ).pipe(Effect.orDie); | 398 | ).pipe(Effect.orDie); |
| 400 | const checkpointStore = yield* tryRuntimePromise("load CheckpointStore service", () => | 399 | const checkpointStore = yield* tryRuntimePromise("load CheckpointStore service", () => |
| 401 | runtime.runPromise(Effect.service(CheckpointStore.CheckpointStore)), | 400 | runtime.runPromise(Effect.service(CheckpointStore.CheckpointStore)), |
| @@ -70,9 +70,7 @@ const makeIntegrationFixture = Effect.gen(function* () { · 69 unmodified lines | |||
| 70 | ), | 70 | ), |
| 71 | ).pipe(Layer.provide(SqlitePersistenceMemory)); | 71 | ).pipe(Layer.provide(SqlitePersistenceMemory)); |
| 72 | 72 | ||
| 73 | const layer = Layer.effect(ProviderService.ProviderService, ProviderService.make()).pipe( | 73 | const layer = ProviderService.layer.pipe(Layer.provide(shared)); |
| 74 | Layer.provide(shared), | ||
| 75 | ); | ||
| 76 | 74 | ||
| 77 | return { | 75 | return { |
| 78 | cwd, | 76 | cwd, |
| @@ -49,10 +49,7 @@ import { · 48 unmodified lines | |||
| 49 | } from "../Services/OrchestrationEngine.ts"; | 49 | } from "../Services/OrchestrationEngine.ts"; |
| 50 | import { CheckpointReactor } from "../Services/CheckpointReactor.ts"; | 50 | import { CheckpointReactor } from "../Services/CheckpointReactor.ts"; |
| 51 | import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts"; | 51 | import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts"; |
| 52 | import { | 52 | import * as ProviderService from "../../provider/ProviderService.ts"; |
| 53 | ProviderService, | ||
| 54 | type ProviderServiceShape, | ||
| 55 | } from "../../provider/Services/ProviderService.ts"; | ||
| 56 | import { checkpointRefForThreadTurn } from "../../checkpointing/Utils.ts"; | 53 | import { checkpointRefForThreadTurn } from "../../checkpointing/Utils.ts"; |
| 57 | import { ServerConfig } from "../../config.ts"; | 54 | import { ServerConfig } from "../../config.ts"; |
| 58 | import * as WorkspaceEntries from "../../workspace/WorkspaceEntries.ts"; | 55 | import * as WorkspaceEntries from "../../workspace/WorkspaceEntries.ts"; |
| @@ -102,7 +99,7 @@ function createProviderServiceHarness( · 43 unmodified lines | |||
| 102 | }, | 99 | }, |
| 103 | ] satisfies ReadonlyArray<ProviderSession>) | 100 | ] satisfies ReadonlyArray<ProviderSession>) |
| 104 | : Effect.succeed([] as ReadonlyArray<ProviderSession>); | 101 | : Effect.succeed([] as ReadonlyArray<ProviderSession>); |
| 105 | const service: ProviderServiceShape = { | 102 | const service: ProviderService.ProviderService["Service"] = { |
| 106 | startSession: () => unsupported(), | 103 | startSession: () => unsupported(), |
| 107 | sendTurn: () => unsupported(), | 104 | sendTurn: () => unsupported(), |
| 108 | interruptTurn: () => unsupported(), | 105 | interruptTurn: () => unsupported(), |
| @@ -328,7 +325,7 @@ describe("CheckpointReactor", () => { · 219 unmodified lines | |||
| 328 | Layer.provideMerge(orchestrationLayer), | 325 | Layer.provideMerge(orchestrationLayer), |
| 329 | Layer.provideMerge(projectionSnapshotLayer), | 326 | Layer.provideMerge(projectionSnapshotLayer), |
| 330 | Layer.provideMerge(RuntimeReceiptBusLive), | 327 | Layer.provideMerge(RuntimeReceiptBusLive), |
| 331 | Layer.provideMerge(Layer.succeed(ProviderService, provider.service)), | 328 | Layer.provideMerge(Layer.succeed(ProviderService.ProviderService, provider.service)), |
| 332 | Layer.provideMerge(vcsStatusBroadcasterLayer), | 329 | Layer.provideMerge(vcsStatusBroadcasterLayer), |
| 333 | Layer.provideMerge(CheckpointStore.layer.pipe(Layer.provide(VcsDriverRegistry.layer))), | 330 | Layer.provideMerge(CheckpointStore.layer.pipe(Layer.provide(VcsDriverRegistry.layer))), |
| 334 | Layer.provideMerge( | 331 | Layer.provideMerge( |
| @@ -25,7 +25,7 @@ import { · 24 unmodified lines | |||
| 25 | resolveThreadWorkspaceCwd, | 25 | resolveThreadWorkspaceCwd, |
| 26 | } from "../../checkpointing/Utils.ts"; | 26 | } from "../../checkpointing/Utils.ts"; |
| 27 | import * as CheckpointStore from "../../checkpointing/CheckpointStore.ts"; | 27 | import * as CheckpointStore from "../../checkpointing/CheckpointStore.ts"; |
| 28 | import { ProviderService } from "../../provider/Services/ProviderService.ts"; | 28 | import * as ProviderService from "../../provider/ProviderService.ts"; |
| 29 | import { CheckpointReactor, type CheckpointReactorShape } from "../Services/CheckpointReactor.ts"; | 29 | import { CheckpointReactor, type CheckpointReactorShape } from "../Services/CheckpointReactor.ts"; |
| 30 | import { OrchestrationEngineService } from "../Services/OrchestrationEngine.ts"; | 30 | import { OrchestrationEngineService } from "../Services/OrchestrationEngine.ts"; |
| 31 | import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts"; | 31 | import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts"; |
| @@ -80,7 +80,7 @@ const make = Effect.gen(function* () { · 48 unmodified lines | |||
| 80 | randomUUID.pipe(Effect.map((uuid) => CommandId.make(`server:${tag}:${uuid}`))); | 80 | randomUUID.pipe(Effect.map((uuid) => CommandId.make(`server:${tag}:${uuid}`))); |
| 81 | const orchestrationEngine = yield* OrchestrationEngineService; | 81 | const orchestrationEngine = yield* OrchestrationEngineService; |
| 82 | const projectionSnapshotQuery = yield* ProjectionSnapshotQuery; | 82 | const projectionSnapshotQuery = yield* ProjectionSnapshotQuery; |
| 83 | const providerService = yield* ProviderService; | 83 | const providerService = yield* ProviderService.ProviderService; |
| 84 | const checkpointStore = yield* CheckpointStore.CheckpointStore; | 84 | const checkpointStore = yield* CheckpointStore.CheckpointStore; |
| 85 | const receiptBus = yield* RuntimeReceiptBus; | 85 | const receiptBus = yield* RuntimeReceiptBus; |
| 86 | const workspaceEntries = yield* WorkspaceEntries.WorkspaceEntries; | 86 | const workspaceEntries = yield* WorkspaceEntries.WorkspaceEntries; |
| @@ -37,10 +37,7 @@ import { ProviderAdapterRequestError } from "../../provider/Errors.ts"; · 36 unmodified lines | |||
| 37 | import { OrchestrationEventStoreLive } from "../../persistence/Layers/OrchestrationEventStore.ts"; | 37 | import { OrchestrationEventStoreLive } from "../../persistence/Layers/OrchestrationEventStore.ts"; |
| 38 | import { OrchestrationCommandReceiptRepositoryLive } from "../../persistence/Layers/OrchestrationCommandReceipts.ts"; | 38 | import { OrchestrationCommandReceiptRepositoryLive } from "../../persistence/Layers/OrchestrationCommandReceipts.ts"; |
| 39 | import { SqlitePersistenceMemory } from "../../persistence/Layers/Sqlite.ts"; | 39 | import { SqlitePersistenceMemory } from "../../persistence/Layers/Sqlite.ts"; |
| 40 | import { | 40 | import * as ProviderService from "../../provider/ProviderService.ts"; |
| 41 | ProviderService, | ||
| 42 | type ProviderServiceShape, | ||
| 43 | } from "../../provider/Services/ProviderService.ts"; | ||
| 44 | import { makeProviderRegistryLayer } from "../../provider/testUtils/providerRegistryMock.ts"; | 41 | import { makeProviderRegistryLayer } from "../../provider/testUtils/providerRegistryMock.ts"; |
| 45 | import { TextGeneration, type TextGenerationShape } from "../../textGeneration/TextGeneration.ts"; | 42 | import { TextGeneration, type TextGenerationShape } from "../../textGeneration/TextGeneration.ts"; |
| 46 | import * as RepositoryIdentityResolver from "../../project/RepositoryIdentityResolver.ts"; | 43 | import * as RepositoryIdentityResolver from "../../project/RepositoryIdentityResolver.ts"; |
| @@ -222,8 +219,12 @@ describe("ProviderCommandReactor", () => { · 175 unmodified lines | |||
| 222 | }), | 219 | }), |
| 223 | ); | 220 | ); |
| 224 | const interruptTurn = vi.fn((_: unknown) => Effect.void); | 221 | const interruptTurn = vi.fn((_: unknown) => Effect.void); |
| 225 | const respondToRequest = vi.fn<ProviderServiceShape["respondToRequest"]>(() => Effect.void); | 222 | const respondToRequest = vi.fn<ProviderService.ProviderService["Service"]["respondToRequest"]>( |
| 226 | const respondToUserInput = vi.fn<ProviderServiceShape["respondToUserInput"]>(() => Effect.void); | 223 | () => Effect.void, |
| 224 | ); | ||
| 225 | const respondToUserInput = vi.fn< | ||
| 226 | ProviderService.ProviderService["Service"]["respondToUserInput"] | ||
| 227 | >(() => Effect.void); | ||
| 227 | const stopSession = vi.fn((input: unknown) => | 228 | const stopSession = vi.fn((input: unknown) => |
| 228 | Effect.sync(() => { | 229 | Effect.sync(() => { |
| 229 | const threadId = | 230 | const threadId = |
| @@ -294,13 +295,15 @@ describe("ProviderCommandReactor", () => { · 64 unmodified lines | |||
| 294 | ]; | 295 | ]; |
| 295 | 296 | ||
| 296 | const unsupported = () => Effect.die(new Error("Unsupported provider call in test")) as never; | 297 | const unsupported = () => Effect.die(new Error("Unsupported provider call in test")) as never; |
| 297 | const service: ProviderServiceShape = { | 298 | const service: ProviderService.ProviderService["Service"] = { |
| 298 | startSession: startSession as ProviderServiceShape["startSession"], | 299 | startSession: startSession as ProviderService.ProviderService["Service"]["startSession"], |
| 299 | sendTurn: sendTurn as ProviderServiceShape["sendTurn"], | 300 | sendTurn: sendTurn as ProviderService.ProviderService["Service"]["sendTurn"], |
| 300 | interruptTurn: interruptTurn as ProviderServiceShape["interruptTurn"], | 301 | interruptTurn: interruptTurn as ProviderService.ProviderService["Service"]["interruptTurn"], |
| 301 | respondToRequest: respondToRequest as ProviderServiceShape["respondToRequest"], | 302 | respondToRequest: |
| 302 | respondToUserInput: respondToUserInput as ProviderServiceShape["respondToUserInput"], | 303 | respondToRequest as ProviderService.ProviderService["Service"]["respondToRequest"], |
| 303 | stopSession: stopSession as ProviderServiceShape["stopSession"], | 304 | respondToUserInput: |
| 305 | respondToUserInput as ProviderService.ProviderService["Service"]["respondToUserInput"], | ||
| 306 | stopSession: stopSession as ProviderService.ProviderService["Service"]["stopSession"], | ||
| 304 | listSessions: () => Effect.succeed(runtimeSessions), | 307 | listSessions: () => Effect.succeed(runtimeSessions), |
| 305 | getCapabilities: (_provider) => | 308 | getCapabilities: (_provider) => |
| 306 | Effect.succeed({ | 309 | Effect.succeed({ |
| @@ -346,7 +349,7 @@ describe("ProviderCommandReactor", () => { · 39 unmodified lines | |||
| 346 | const layer = ProviderCommandReactorLive.pipe( | 349 | const layer = ProviderCommandReactorLive.pipe( |
| 347 | Layer.provideMerge(orchestrationLayer), | 350 | Layer.provideMerge(orchestrationLayer), |
| 348 | Layer.provideMerge(projectionSnapshotLayer), | 351 | Layer.provideMerge(projectionSnapshotLayer), |
| 349 | Layer.provideMerge(Layer.succeed(ProviderService, service)), | 352 | Layer.provideMerge(Layer.succeed(ProviderService.ProviderService, service)), |
| 350 | Layer.provideMerge(makeProviderRegistryLayer(providerSnapshots as never)), | 353 | Layer.provideMerge(makeProviderRegistryLayer(providerSnapshots as never)), |
| 351 | Layer.provideMerge( | 354 | Layer.provideMerge( |
| 352 | Layer.mock(GitWorkflowService.GitWorkflowService)({ | 355 | Layer.mock(GitWorkflowService.GitWorkflowService)({ |
| @@ -30,8 +30,8 @@ import { increment, orchestrationEventsProcessedTotal } from "../../observabilit · 29 unmodified lines | |||
| 30 | import { ProviderAdapterRequestError } from "../../provider/Errors.ts"; | 30 | import { ProviderAdapterRequestError } from "../../provider/Errors.ts"; |
| 31 | import type { ProviderServiceError } from "../../provider/Errors.ts"; | 31 | import type { ProviderServiceError } from "../../provider/Errors.ts"; |
| 32 | import { TextGeneration } from "../../textGeneration/TextGeneration.ts"; | 32 | import { TextGeneration } from "../../textGeneration/TextGeneration.ts"; |
| 33 | import { ProviderService } from "../../provider/Services/ProviderService.ts"; | 33 | import * as ProviderService from "../../provider/ProviderService.ts"; |
| 34 | import { ProviderRegistry } from "../../provider/Services/ProviderRegistry.ts"; | 34 | import * as ProviderRegistry from "../../provider/ProviderRegistry.ts"; |
| 35 | import { OrchestrationEngineService } from "../Services/OrchestrationEngine.ts"; | 35 | import { OrchestrationEngineService } from "../Services/OrchestrationEngine.ts"; |
| 36 | import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts"; | 36 | import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts"; |
| 37 | import { | 37 | import { |
| @@ -190,8 +190,8 @@ const make = Effect.gen(function* () { · 152 unmodified lines | |||
| 190 | const crypto = yield* Crypto.Crypto; | 190 | const crypto = yield* Crypto.Crypto; |
| 191 | const orchestrationEngine = yield* OrchestrationEngineService; | 191 | const orchestrationEngine = yield* OrchestrationEngineService; |
| 192 | const projectionSnapshotQuery = yield* ProjectionSnapshotQuery; | 192 | const projectionSnapshotQuery = yield* ProjectionSnapshotQuery; |
| 193 | const providerService = yield* ProviderService; | 193 | const providerService = yield* ProviderService.ProviderService; |
| 194 | const providerRegistry = yield* ProviderRegistry; | 194 | const providerRegistry = yield* ProviderRegistry.ProviderRegistry; |
| 195 | const gitWorkflow = yield* GitWorkflowService; | 195 | const gitWorkflow = yield* GitWorkflowService; |
| 196 | const vcsStatusBroadcaster = yield* VcsStatusBroadcaster; | 196 | const vcsStatusBroadcaster = yield* VcsStatusBroadcaster; |
| 197 | const textGeneration = yield* TextGeneration; | 197 | const textGeneration = yield* TextGeneration; |
| @@ -35,10 +35,7 @@ import { afterEach, describe, expect, it } from "vite-plus/test"; · 34 unmodified lines | |||
| 35 | import { OrchestrationEventStoreLive } from "../../persistence/Layers/OrchestrationEventStore.ts"; | 35 | import { OrchestrationEventStoreLive } from "../../persistence/Layers/OrchestrationEventStore.ts"; |
| 36 | import { OrchestrationCommandReceiptRepositoryLive } from "../../persistence/Layers/OrchestrationCommandReceipts.ts"; | 36 | import { OrchestrationCommandReceiptRepositoryLive } from "../../persistence/Layers/OrchestrationCommandReceipts.ts"; |
| 37 | import { SqlitePersistenceMemory } from "../../persistence/Layers/Sqlite.ts"; | 37 | import { SqlitePersistenceMemory } from "../../persistence/Layers/Sqlite.ts"; |
| 38 | import { | 38 | import * as ProviderService from "../../provider/ProviderService.ts"; |
| 39 | ProviderService, | ||
| 40 | type ProviderServiceShape, | ||
| 41 | } from "../../provider/Services/ProviderService.ts"; | ||
| 42 | import * as RepositoryIdentityResolver from "../../project/RepositoryIdentityResolver.ts"; | 39 | import * as RepositoryIdentityResolver from "../../project/RepositoryIdentityResolver.ts"; |
| 43 | import { OrchestrationEngineLive } from "./OrchestrationEngine.ts"; | 40 | import { OrchestrationEngineLive } from "./OrchestrationEngine.ts"; |
| 44 | import { OrchestrationProjectionPipelineLive } from "./ProjectionPipeline.ts"; | 41 | import { OrchestrationProjectionPipelineLive } from "./ProjectionPipeline.ts"; |
| @@ -97,7 +94,7 @@ function createProviderServiceHarness() { · 52 unmodified lines | |||
| 97 | const runtimeSessions: ProviderSession[] = []; | 94 | const runtimeSessions: ProviderSession[] = []; |
| 98 | 95 | ||
| 99 | const unsupported = () => Effect.die(new Error("Unsupported provider call in test")) as never; | 96 | const unsupported = () => Effect.die(new Error("Unsupported provider call in test")) as never; |
| 100 | const service: ProviderServiceShape = { | 97 | const service: ProviderService.ProviderService["Service"] = { |
| 101 | startSession: () => unsupported(), | 98 | startSession: () => unsupported(), |
| 102 | sendTurn: () => unsupported(), | 99 | sendTurn: () => unsupported(), |
| 103 | interruptTurn: () => unsupported(), | 100 | interruptTurn: () => unsupported(), |
| @@ -237,7 +234,7 @@ describe("ProviderRuntimeIngestion", () => { · 133 unmodified lines | |||
| 237 | Layer.provideMerge(orchestrationLayer), | 234 | Layer.provideMerge(orchestrationLayer), |
| 238 | Layer.provideMerge(projectionSnapshotLayer), | 235 | Layer.provideMerge(projectionSnapshotLayer), |
| 239 | Layer.provideMerge(SqlitePersistenceMemory), | 236 | Layer.provideMerge(SqlitePersistenceMemory), |
| 240 | Layer.provideMerge(Layer.succeed(ProviderService, provider.service)), | 237 | Layer.provideMerge(Layer.succeed(ProviderService.ProviderService, provider.service)), |
| 241 | Layer.provideMerge(makeTestServerSettingsLayer(options?.serverSettings)), | 238 | Layer.provideMerge(makeTestServerSettingsLayer(options?.serverSettings)), |
| 242 | Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd())), | 239 | Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd())), |
| 243 | Layer.provideMerge(NodeServices.layer), | 240 | Layer.provideMerge(NodeServices.layer), |
| @@ -27,7 +27,7 @@ import * as Option from "effect/Option"; · 26 unmodified lines | |||
| 27 | import * as Stream from "effect/Stream"; | 27 | import * as Stream from "effect/Stream"; |
| 28 | import { makeDrainableWorker } from "@t3tools/shared/DrainableWorker"; | 28 | import { makeDrainableWorker } from "@t3tools/shared/DrainableWorker"; |
| 29 | 29 | ||
| 30 | import { ProviderService } from "../../provider/Services/ProviderService.ts"; | 30 | import * as ProviderService from "../../provider/ProviderService.ts"; |
| 31 | import { ProjectionTurnRepository } from "../../persistence/Services/ProjectionTurns.ts"; | 31 | import { ProjectionTurnRepository } from "../../persistence/Services/ProjectionTurns.ts"; |
| 32 | import { ProjectionTurnRepositoryLive } from "../../persistence/Layers/ProjectionTurns.ts"; | 32 | import { ProjectionTurnRepositoryLive } from "../../persistence/Layers/ProjectionTurns.ts"; |
| 33 | import { isGitRepository } from "../../git/Utils.ts"; | 33 | import { isGitRepository } from "../../git/Utils.ts"; |
| @@ -631,7 +631,7 @@ const make = Effect.gen(function* () { · 597 unmodified lines | |||
| 631 | const crypto = yield* Crypto.Crypto; | 631 | const crypto = yield* Crypto.Crypto; |
| 632 | const orchestrationEngine = yield* OrchestrationEngineService; | 632 | const orchestrationEngine = yield* OrchestrationEngineService; |
| 633 | const projectionSnapshotQuery = yield* ProjectionSnapshotQuery; | 633 | const projectionSnapshotQuery = yield* ProjectionSnapshotQuery; |
| 634 | const providerService = yield* ProviderService; | 634 | const providerService = yield* ProviderService.ProviderService; |
| 635 | const projectionTurnRepository = yield* ProjectionTurnRepository; | 635 | const projectionTurnRepository = yield* ProjectionTurnRepository; |
| 636 | const serverSettingsService = yield* ServerSettingsService; | 636 | const serverSettingsService = yield* ServerSettingsService; |
| 637 | const providerCommandId = (event: ProviderRuntimeEvent, tag: string) => | 637 | const providerCommandId = (event: ProviderRuntimeEvent, tag: string) => |
| @@ -5,7 +5,7 @@ import * as Effect from "effect/Effect"; · 4 unmodified lines | |||
| 5 | import * as Layer from "effect/Layer"; | 5 | import * as Layer from "effect/Layer"; |
| 6 | import * as Stream from "effect/Stream"; | 6 | import * as Stream from "effect/Stream"; |
| 7 | 7 | ||
| 8 | import { ProviderService } from "../../provider/Services/ProviderService.ts"; | 8 | import * as ProviderService from "../../provider/ProviderService.ts"; |
| 9 | import * as TerminalManager from "../../terminal/Manager.ts"; | 9 | import * as TerminalManager from "../../terminal/Manager.ts"; |
| 10 | import { OrchestrationEngineService } from "../Services/OrchestrationEngine.ts"; | 10 | import { OrchestrationEngineService } from "../Services/OrchestrationEngine.ts"; |
| 11 | import { | 11 | import { |
| @@ -38,7 +38,7 @@ export const logCleanupCauseUnlessInterrupted = <R, E>({ · 26 unmodified lines | |||
| 38 | 38 | ||
| 39 | const make = Effect.gen(function* () { | 39 | const make = Effect.gen(function* () { |
| 40 | const orchestrationEngine = yield* OrchestrationEngineService; | 40 | const orchestrationEngine = yield* OrchestrationEngineService; |
| 41 | const providerService = yield* ProviderService; | 41 | const providerService = yield* ProviderService.ProviderService; |
| 42 | const terminalManager = yield* TerminalManager.TerminalManager; | 42 | const terminalManager = yield* TerminalManager.TerminalManager; |
| 43 | 43 | ||
| 44 | const stopProviderSession = (threadId: ThreadDeletedEvent["payload"]["threadId"]) => | 44 | const stopProviderSession = (threadId: ThreadDeletedEvent["payload"]["threadId"]) => |
| @@ -16,6 +16,31 @@ const decodeOpenCodeSettings = Schema.decodeSync(OpenCodeSettings); · 15 unmodified lines | |||
| 16 | 16 | ||
| 17 | const DEFAULT_VERSION_STDOUT = "opencode 1.14.19\n"; | 17 | const DEFAULT_VERSION_STDOUT = "opencode 1.14.19\n"; |
| 18 | 18 | ||
| 19 | it("keeps OpenCode runtime causes separate from stable messages", () => { | ||
| 20 | const cause = new Error("sdk response included sensitive diagnostics"); | ||
| 21 | const error = new OpenCodeRuntime.OpenCodeRuntimeError({ | ||
| 22 | operation: "provider.list", | ||
| 23 | detail: cause.message, | ||
| 24 | cause, | ||
| 25 | }); | ||
| 26 | |||
| 27 | assert.equal(error.cause, cause); | ||
| 28 | assert.equal(error.message, "OpenCode runtime operation provider.list failed."); | ||
| 29 | assert.doesNotMatch(error.message, /sensitive diagnostics/u); | ||
| 30 | |||
| 31 | const processExit = new OpenCodeRuntime.OpenCodeRuntimeError({ | ||
| 32 | operation: "startOpenCodeServerProcess", | ||
| 33 | detail: "OpenCode server exited before startup completed.", | ||
| 34 | exitCode: 1, | ||
| 35 | stdout: "startup output", | ||
| 36 | stderr: "startup failure", | ||
| 37 | }); | ||
| 38 | assert.equal(processExit.cause, undefined); | ||
| 39 | assert.equal(processExit.exitCode, 1); | ||
| 40 | assert.equal(processExit.stdout, "startup output"); | ||
| 41 | assert.equal(processExit.stderr, "startup failure"); | ||
| 42 | }); | ||
| 43 | |||
| 19 | /** | 44 | /** |
| 20 | * The legacy `OpenCodeProviderLive` Layer + `OpenCodeProvider` service tag | 45 | * The legacy `OpenCodeProviderLive` Layer + `OpenCodeProvider` service tag |
| 21 | * are deleted. The snapshot-producing logic they wrapped now lives in the | 46 | * are deleted. The snapshot-producing logic they wrapped now lives in the |
| @@ -1,6 +0,0 @@ | |||
| 1 | // Compatibility shim for the intentionally excluded orchestration harness. | ||
| 2 | import * as Canonical from "../ProviderEventLoggers.ts"; | ||
| 3 | |||
| 4 | export const ProviderEventLoggersLive = Canonical.layer; | ||
| 5 | export const ProviderEventLoggers = Canonical.ProviderEventLoggers; | ||
| 6 | export const NoOpProviderEventLoggers = Canonical.NoOpProviderEventLoggers; | ||
| @@ -1,11 +0,0 @@ | |||
| 1 | // Compatibility shim for the intentionally excluded orchestration harness. | ||
| 2 | import * as Layer from "effect/Layer"; | ||
| 3 | |||
| 4 | import * as ProviderService from "../ProviderService.ts"; | ||
| 5 | |||
| 6 | export type ProviderServiceLiveOptions = ProviderService.ProviderServiceOptions; | ||
| 7 | |||
| 8 | export const ProviderServiceLive = ProviderService.layer; | ||
| 9 | |||
| 10 | export const makeProviderServiceLive = (options?: ProviderService.ProviderServiceOptions) => | ||
| 11 | Layer.effect(ProviderService.ProviderService, ProviderService.make(options)); | ||
| @@ -1,6 +0,0 @@ | |||
| 1 | // Compatibility shim for the intentionally excluded orchestration harness. | ||
| 2 | import * as ProviderSessionDirectory from "../ProviderSessionDirectory.ts"; | ||
| 3 | |||
| 4 | export const ProviderSessionDirectoryLive = ProviderSessionDirectory.layer; | ||
| 5 | |||
| 6 | export const makeProviderSessionDirectoryLive = () => ProviderSessionDirectory.layer; | ||
| @@ -12,11 +12,16 @@ · 11 unmodified lines | |||
| 12 | * - `getByInstance` / `listInstances` — new per-instance routing. Callers | 12 | * - `getByInstance` / `listInstances` — new per-instance routing. Callers |
| 13 | * that already know an `instanceId` (threads, sessions, events) | 13 | * that already know an `instanceId` (threads, sessions, events) |
| 14 | * should prefer these. | 14 | * should prefer these. |
| 15 | * - `listProviders` — legacy kind-keyed routing for default instances | ||
| 15 | * (`defaultInstanceIdForDriver(kind) === kind`), matching the pre-Slice-D | 16 | * (`defaultInstanceIdForDriver(kind) === kind`), matching the pre-Slice-D |
| 16 | * behaviour. New code should not grow additional callers of the kind-keyed | 17 | * behaviour. New code should not grow additional callers of the kind-keyed |
| 17 | * methods; they exist so the settings UI, WS refresh RPC, and a handful | 18 | * methods; they exist so the settings UI, WS refresh RPC, and a handful |
| 18 | * of legacy persisted rows can still be routed during the rollout. | 19 | * of legacy persisted rows can still be routed during the rollout. |
| 19 | * | 20 | * |
| 21 | * Adapter lookups are resolved dynamically through `ProviderInstanceRegistry`. | ||
| 22 | * Settings-driven hot reload is therefore visible immediately without | ||
| 23 | * rebuilding this facade. | ||
| 24 | * | ||
| 20 | * @module ProviderAdapterRegistry | 25 | * @module ProviderAdapterRegistry |
| 21 | */ | 26 | */ |
| 22 | import { | 27 | import { |
| @@ -50,7 +55,10 @@ export class ProviderAdapterRegistry extends Context.Service< · 27 unmodified lines | |||
| 50 | { | 55 | { |
| 51 | /** | 56 | /** |
| 52 | * Resolve the adapter for a specific instance id. Returns | 57 | * Resolve the adapter for a specific instance id. Returns |
| 53 | * `ProviderUnsupportedError` if no such live instance is registered. | 58 | * `ProviderUnsupportedError` if no such instance is currently registered |
| 59 | * (which covers "never configured" and "configured but the driver is | ||
| 60 | * unavailable in this build"; both surface the same failure to callers | ||
| 61 | * that expect a working adapter). | ||
| 54 | */ | 62 | */ |
| 55 | readonly getByInstance: ( | 63 | readonly getByInstance: ( |
| 56 | instanceId: ProviderInstanceId, | 64 | instanceId: ProviderInstanceId, |
| @@ -62,28 +70,33 @@ export class ProviderAdapterRegistry extends Context.Service< · 5 unmodified lines | |||
| 62 | ) => Effect.Effect<ProviderInstanceRoutingInfo, ProviderUnsupportedError>; | 70 | ) => Effect.Effect<ProviderInstanceRoutingInfo, ProviderUnsupportedError>; |
| 63 | 71 | ||
| 64 | /** | 72 | /** |
| 65 | * List every live instance id. Unavailable shadow instances are excluded | 73 | * List all live instance ids. Excludes unavailable/shadow instances because |
| 66 | * because callers use these ids with `getByInstance`. | 74 | * callers of this method want something they can pass to `getByInstance`. |
| 67 | */ | 75 | */ |
| 68 | readonly listInstances: () => Effect.Effect<ReadonlyArray<ProviderInstanceId>>; | 76 | readonly listInstances: () => Effect.Effect<ReadonlyArray<ProviderInstanceId>>; |
| 69 | 77 | ||
| 70 | /** | 78 | /** |
| 71 | * List provider kinds whose default instance is currently registered. | 79 | * List provider kinds whose default instance is currently registered. |
| 72 | * | 80 | * |
| 73 | * @deprecated Prefer `listInstances`; this remains for migration-era | 81 | * @deprecated Prefer `listInstances`. Retained for migration-era call sites |
| 74 | * callers that still address providers by driver kind. | 82 | * that iterate providers to build UI or metrics. |
| 75 | */ | 83 | */ |
| 76 | readonly listProviders: () => Effect.Effect<ReadonlyArray<ProviderDriverKind>>; | 84 | readonly listProviders: () => Effect.Effect<ReadonlyArray<ProviderDriverKind>>; |
| 77 | 85 | ||
| 78 | /** | 86 | /** |
| 79 | * Emits whenever the live instance set changes. Consumers should re-read | 87 | * Change notification stream mirroring |
| 80 | * `listInstances` and reconcile their per-instance subscriptions. | 88 | * `ProviderInstanceRegistry.streamChanges`. Emits one `void` tick whenever |
| 89 | * the set of live instances changes. Consumers that fan out | ||
| 90 | * `adapter.streamEvents` per instance re-pull `listInstances` on each tick | ||
| 91 | * and fork subscriptions for instances they have not seen yet. | ||
| 81 | */ | 92 | */ |
| 82 | readonly streamChanges: Stream.Stream<void>; | 93 | readonly streamChanges: Stream.Stream<void>; |
| 83 | 94 | ||
| 84 | /** | 95 | /** |
| 85 | * Acquire the change subscription synchronously in the caller's scope, | 96 | * Acquire a change subscription synchronously in the caller's current |
| 86 | * avoiding the publish race inherent in forking `Stream.fromPubSub`. | 97 | * fiber. Consumers that must avoid missing a publish between initial |
| 98 | * reconciliation and watcher startup should use this, then fork | ||
| 99 | * `Stream.fromSubscription`. | ||
| 87 | */ | 100 | */ |
| 88 | readonly subscribeChanges: Effect.Effect<PubSub.Subscription<void>, never, Scope.Scope>; | 101 | readonly subscribeChanges: Effect.Effect<PubSub.Subscription<void>, never, Scope.Scope>; |
| 89 | } | 102 | } |
| @@ -130,6 +143,8 @@ export const make = Effect.gen(function* () { · 40 unmodified lines | |||
| 130 | const kinds = new Set<ProviderDriverKind>(); | 143 | const kinds = new Set<ProviderDriverKind>(); |
| 131 | for (const instance of instances) { | 144 | for (const instance of instances) { |
| 132 | if (instance.instanceId === defaultInstanceIdForDriver(instance.driverKind)) { | 145 | if (instance.instanceId === defaultInstanceIdForDriver(instance.driverKind)) { |
| 146 | // Only default instances appear through this legacy view; custom | ||
| 147 | // instances such as `codex_personal` have no driver-kind identity. | ||
| 133 | kinds.add(instance.driverKind); | 148 | kinds.add(instance.driverKind); |
| 134 | } | 149 | } |
| 135 | } | 150 | } |
| @@ -142,6 +157,8 @@ export const make = Effect.gen(function* () { · 6 unmodified lines | |||
| 142 | getInstanceInfo, | 157 | getInstanceInfo, |
| 143 | listInstances, | 158 | listInstances, |
| 144 | listProviders, | 159 | listProviders, |
| 160 | // The facade owns no state; the instance registry already coalesces | ||
| 161 | // add, remove, and rebuild notifications into a single change channel. | ||
| 145 | streamChanges: registry.streamChanges, | 162 | streamChanges: registry.streamChanges, |
| 146 | subscribeChanges: registry.subscribeChanges, | 163 | subscribeChanges: registry.subscribeChanges, |
| 147 | }); | 164 | }); |
| @@ -58,38 +58,46 @@ export class ProviderInstanceRegistry extends Context.Service< · 57 unmodified lines | |||
| 58 | ProviderInstanceRegistry, | 58 | ProviderInstanceRegistry, |
| 59 | { | 59 | { |
| 60 | /** | 60 | /** |
| 61 | * Look up one instance by id. Unknown ids return `undefined`; callers map | 61 | * Look up one instance by id. Returns `undefined` (not `Option`) when the |
| 62 | * that absence to the appropriate domain error. | 62 | * id is unknown; callers branch on absence and emit the appropriate domain |
| 63 | * error. | ||
| 63 | */ | 64 | */ |
| 64 | readonly getInstance: ( | 65 | readonly getInstance: ( |
| 65 | instanceId: ProviderInstanceId, | 66 | instanceId: ProviderInstanceId, |
| 66 | ) => Effect.Effect<ProviderInstance | undefined>; | 67 | ) => Effect.Effect<ProviderInstance | undefined>; |
| 67 | 68 | ||
| 68 | /** | 69 | /** |
| 69 | * Every successfully materialized instance, in stable settings-author | 70 | * Every available (driver-registered, successfully created) instance, in |
| 70 | * order. | 71 | * stable settings-author order. |
| 71 | */ | 72 | */ |
| 72 | readonly listInstances: Effect.Effect<ReadonlyArray<ProviderInstance>>; | 73 | readonly listInstances: Effect.Effect<ReadonlyArray<ProviderInstance>>; |
| 73 | 74 | ||
| 74 | /** | 75 | /** |
| 75 | * Shadow snapshots for unknown drivers or invalid configurations, ready | 76 | * Wire-shape shadow snapshots for instances whose driver is unknown to this |
| 76 | * to merge into `ProviderRegistry` output. | 77 | * build or whose config failed to decode. Suitable for merging directly |
| 78 | * into `ProviderRegistry` output. | ||
| 77 | */ | 79 | */ |
| 78 | readonly listUnavailable: Effect.Effect<ReadonlyArray<ServerProvider>>; | 80 | readonly listUnavailable: Effect.Effect<ReadonlyArray<ServerProvider>>; |
| 79 | 81 | ||
| 80 | /** | 82 | /** |
| 81 | * Emits after the registry adds, removes, or rebuilds instances. The | 83 | * Push notification stream emitted whenever the registry's contents change. |
| 82 | * payload is `void` because consumers re-read both registry lists. | 84 | * The payload is `void` because consumers always re-pull `listInstances` |
| 85 | * and `listUnavailable` together. | ||
| 83 | * | 86 | * |
| 84 | * `Stream.fromPubSub` subscribes only when execution begins, so a newly | 87 | * `Stream.fromPubSub` defers `PubSub.subscribe` until the stream starts |
| 85 | * forked consumer can miss a publish. Consumers that cannot tolerate that | 88 | * running, so forking a consumer races the next publish. Hot-reload |
| 86 | * gap should acquire `subscribeChanges` first. | 89 | * consumers that cannot miss a publish should use `subscribeChanges` |
| 90 | * instead, which acquires the subscription synchronously before the | ||
| 91 | * consumer loop is forked. | ||
| 87 | */ | 92 | */ |
| 88 | readonly streamChanges: Stream.Stream<void>; | 93 | readonly streamChanges: Stream.Stream<void>; |
| 89 | 94 | ||
| 90 | /** | 95 | /** |
| 91 | * Acquire a scoped PubSub subscription synchronously before forking the | 96 | * Acquire a scoped subscription to the change channel synchronously in the |
| 92 | * consumer loop, ensuring subsequent publishes cannot land in a gap. | 97 | * caller's fiber. Consumers typically `yield*` this in the same fiber that |
| 98 | * forks their consumer loop, then drain it with `PubSub.take` or | ||
| 99 | * `Stream.fromSubscription`. Because the subscription is registered before | ||
| 100 | * this effect returns, no subsequent publish can land in a gap. | ||
| 93 | */ | 101 | */ |
| 94 | readonly subscribeChanges: Effect.Effect<PubSub.Subscription<void>, never, Scope.Scope>; | 102 | readonly subscribeChanges: Effect.Effect<PubSub.Subscription<void>, never, Scope.Scope>; |
| 95 | } | 103 | } |
| @@ -107,9 +115,14 @@ export class ProviderInstanceRegistryMutator extends Context.Service< · 11 unmodified lines | |||
| 107 | ProviderInstanceRegistryMutator, | 115 | ProviderInstanceRegistryMutator, |
| 108 | { | 116 | { |
| 109 | /** | 117 | /** |
| 110 | * Reconcile the live registry with a new configuration map. Individual | 118 | * Bring the live registry in line with the supplied config map. The |
| 111 | * driver creation failures become unavailable shadow snapshots so the | 119 | * operation closes removed or replaced scopes before creating replacements |
| 112 | * settings watcher itself never fails. | 120 | * and publishes a single change tick after the batch. Reapplying an |
| 121 | * unchanged map is a no-op. | ||
| 122 | * | ||
| 123 | * The effect never fails: individual driver creation failures become | ||
| 124 | * unavailable shadow snapshots, keeping settings-watcher loops alive when | ||
| 125 | * one entry is invalid. | ||
| 113 | */ | 126 | */ |
| 114 | readonly reconcile: (configMap: ProviderInstanceConfigMap) => Effect.Effect<void>; | 127 | readonly reconcile: (configMap: ProviderInstanceConfigMap) => Effect.Effect<void>; |
| 115 | } | 128 | } |
| @@ -64,13 +64,18 @@ export class ProviderRegistry extends Context.Service< · 63 unmodified lines | |||
| 64 | ProviderRegistry, | 64 | ProviderRegistry, |
| 65 | { | 65 | { |
| 66 | /** | 66 | /** |
| 67 | * Read the latest snapshot for every configured instance. Multiple | 67 | * Read the latest provider snapshots for every configured instance. |
| 68 | * snapshots can share a driver kind and are distinguished by instance id. | 68 | * Multiple snapshots may share the same driver kind and disambiguate via |
| 69 | * `instanceId`. | ||
| 69 | */ | 70 | */ |
| 70 | readonly getProviders: Effect.Effect<ReadonlyArray<ServerProvider>>; | 71 | readonly getProviders: Effect.Effect<ReadonlyArray<ServerProvider>>; |
| 71 | 72 | ||
| 72 | /** | 73 | /** |
| 73 | * Refresh every provider, or the default instance for one driver kind. | 74 | * Refresh all providers, or the default instance of the specified kind |
| 75 | * when supplied. | ||
| 76 | * | ||
| 77 | * Retained for compatibility with legacy call sites such as the WS refresh | ||
| 78 | * RPC and orchestration metrics. | ||
| 74 | * | 79 | * |
| 75 | * @deprecated Prefer `refreshInstance` for new call sites. | 80 | * @deprecated Prefer `refreshInstance` for new call sites. |
| 76 | */ | 81 | */ |
| @@ -80,15 +85,16 @@ export class ProviderRegistry extends Context.Service< · 3 unmodified lines | |||
| 80 | 85 | ||
| 81 | /** | 86 | /** |
| 82 | * Refresh one configured instance. Unknown ids resolve to the current | 87 | * Refresh one configured instance. Unknown ids resolve to the current |
| 83 | * cached list to preserve the legacy transport behavior. | 88 | * cached list, matching the legacy `refresh` behavior so transport layers |
| 89 | * do not have to special-case unknown ids. | ||
| 84 | */ | 90 | */ |
| 85 | readonly refreshInstance: ( | 91 | readonly refreshInstance: ( |
| 86 | instanceId: ProviderInstanceId, | 92 | instanceId: ProviderInstanceId, |
| 87 | ) => Effect.Effect<ReadonlyArray<ServerProvider>>; | 93 | ) => Effect.Effect<ReadonlyArray<ServerProvider>>; |
| 88 | 94 | ||
| 89 | /** | 95 | /** |
| 90 | * Resolve maintenance capabilities for a live instance, falling back to | 96 | * Resolve maintenance capabilities for one live provider instance, |
| 91 | * manual-only capabilities when that instance is unavailable. | 97 | * falling back to manual-only capabilities when it is unavailable. |
| 92 | */ | 98 | */ |
| 93 | readonly getProviderMaintenanceCapabilitiesForInstance: ( | 99 | readonly getProviderMaintenanceCapabilitiesForInstance: ( |
| 94 | instanceId: ProviderInstanceId, | 100 | instanceId: ProviderInstanceId, |
| @@ -96,8 +102,10 @@ export class ProviderRegistry extends Context.Service< · 1 unmodified line | |||
| 96 | ) => Effect.Effect<ProviderMaintenanceCapabilities>; | 102 | ) => Effect.Effect<ProviderMaintenanceCapabilities>; |
| 97 | 103 | ||
| 98 | /** | 104 | /** |
| 99 | * Apply volatile maintenance state for one instance. This state is not | 105 | * Apply volatile maintenance-action state to one configured instance. This |
| 100 | * persisted; update actions are projected onto `ServerProvider.updateState`. | 106 | * state is never persisted. Today only update actions are projected onto |
| 107 | * `ServerProvider.updateState`; install/auth actions can extend this map | ||
| 108 | * without adding driver-scoped APIs. | ||
| 101 | */ | 109 | */ |
| 102 | readonly setProviderMaintenanceActionState: (input: { | 110 | readonly setProviderMaintenanceActionState: (input: { |
| 103 | readonly instanceId: ProviderInstanceId; | 111 | readonly instanceId: ProviderInstanceId; |
| @@ -105,7 +113,10 @@ export class ProviderRegistry extends Context.Service< · 1 unmodified line | |||
| 105 | readonly state: ServerProviderUpdateState | null; | 113 | readonly state: ServerProviderUpdateState | null; |
| 106 | }) => Effect.Effect<ReadonlyArray<ServerProvider>>; | 114 | }) => Effect.Effect<ReadonlyArray<ServerProvider>>; |
| 107 | 115 | ||
| 108 | /** Emits the full materialized provider list after each aggregated change. */ | 116 | /** |
| 117 | * Stream of provider snapshot updates, one emission per aggregated change. | ||
| 118 | * The array contains the full current state. | ||
| 119 | */ | ||
| 109 | readonly streamChanges: Stream.Stream<ReadonlyArray<ServerProvider>>; | 120 | readonly streamChanges: Stream.Stream<ReadonlyArray<ServerProvider>>; |
| 110 | } | 121 | } |
| 111 | >()("t3/provider/ProviderRegistry") {} | 122 | >()("t3/provider/ProviderRegistry") {} |
| @@ -671,19 +682,6 @@ export const make = Effect.gen(function* () { · 559 unmodified lines | |||
| 671 | // and pending fallbacks fill the gaps. | 682 | // and pending fallbacks fill the gaps. |
| 672 | yield* upsertProviders(fallbackProviders, { publish: false }); | 683 | yield* upsertProviders(fallbackProviders, { publish: false }); |
| 673 | // Subscribe to registry mutations BEFORE running the initial sync. | 684 | // Subscribe to registry mutations BEFORE running the initial sync. |
| 674 | // `subscribeChanges` acquires the dequeue synchronously in this | ||
| 675 | // fibre; the subscription is active the instant this `yield*` | ||
| 676 | // returns. Forking the consumer loop later cannot lose a publish | ||
| 677 | // because no publish can reach a not-yet-subscribed dequeue. | ||
| 678 | // | ||
| 679 | // (Contrast with the pre-fix code that did | ||
| 680 | // `Stream.runForEach(instanceRegistry.streamChanges, …).pipe(Effect.forkScoped)`. | ||
| 681 | // `Stream.fromPubSub` defers `PubSub.subscribe` to stream start, | ||
| 682 | // and `forkScoped` only schedules the fibre — so a reconcile that | ||
| 683 | // published between "fibre scheduled" and "fibre starts running" | ||
| 684 | // was dropped, which made any settings change that replaced an | ||
| 685 | // instance never propagate to the aggregator's `providersRef`.) | ||
| 686 | // Subscribe to registry mutations BEFORE running the initial sync. | ||
| 687 | // `subscribeChanges` acquires the `PubSub.Subscription` synchronously | 685 | // `subscribeChanges` acquires the `PubSub.Subscription` synchronously |
| 688 | // in this fibre; the subscription is registered with the PubSub the | 686 | // in this fibre; the subscription is registered with the PubSub the |
| 689 | // instant this `yield*` returns, so any subsequent publish is | 687 | // instant this `yield*` returns, so any subsequent publish is |
| @@ -55,10 +55,13 @@ import { · 54 unmodified lines | |||
| 55 | } from "../persistence/Layers/Sqlite.ts"; | 55 | } from "../persistence/Layers/Sqlite.ts"; |
| 56 | import * as ServerSettings from "../serverSettings.ts"; | 56 | import * as ServerSettings from "../serverSettings.ts"; |
| 57 | import * as AnalyticsService from "../telemetry/AnalyticsService.ts"; | 57 | import * as AnalyticsService from "../telemetry/AnalyticsService.ts"; |
| 58 | import { makeAdapterRegistryMock } from "./testUtils/providerAdapterRegistryMock.ts"; | ||
| 58 | 59 | ||
| 59 | const makeProviderServiceLive = (options?: ProviderService.ProviderServiceOptions) => | 60 | // Several tests build two ProviderService instances in one scope to simulate a |
| 61 | // process restart. Construct a fresh Layer for each build so Layer memoization | ||
| 62 | // cannot reuse the first service instance. | ||
| 63 | const makeFreshProviderServiceLayer = (options?: ProviderService.ProviderServiceOptions) => | ||
| 60 | Layer.effect(ProviderService.ProviderService, ProviderService.make(options)); | 64 | Layer.effect(ProviderService.ProviderService, ProviderService.make(options)); |
| 61 | import { makeAdapterRegistryMock } from "./testUtils/providerAdapterRegistryMock.ts"; | ||
| 62 | 65 | ||
| 63 | const defaultServerSettingsLayer = ServerSettings.ServerSettingsService.layerTest(); | 66 | const defaultServerSettingsLayer = ServerSettings.ServerSettingsService.layerTest(); |
| 64 | 67 | ||
| @@ -289,7 +292,7 @@ function makeProviderServiceLayer() { · 224 unmodified lines | |||
| 289 | 292 | ||
| 290 | const layer = it.layer( | 293 | const layer = it.layer( |
| 291 | Layer.mergeAll( | 294 | Layer.mergeAll( |
| 292 | makeProviderServiceLive().pipe( | 295 | makeFreshProviderServiceLayer().pipe( |
| 293 | Layer.provide(providerAdapterLayer), | 296 | Layer.provide(providerAdapterLayer), |
| 294 | Layer.provide(directoryLayer), | 297 | Layer.provide(directoryLayer), |
| 295 | Layer.provide(defaultServerSettingsLayer), | 298 | Layer.provide(defaultServerSettingsLayer), |
| @@ -342,7 +345,7 @@ it.effect("ProviderServiceLive catches stopAll failures during shutdown", () => · 46 unmodified lines | |||
| 342 | Layer.provide(runtimeRepositoryLayer), | 345 | Layer.provide(runtimeRepositoryLayer), |
| 343 | ); | 346 | ); |
| 344 | const providerLayer = Layer.mergeAll( | 347 | const providerLayer = Layer.mergeAll( |
| 345 | makeProviderServiceLive().pipe( | 348 | makeFreshProviderServiceLayer().pipe( |
| 346 | Layer.provide(providerAdapterLayer), | 349 | Layer.provide(providerAdapterLayer), |
| 347 | Layer.provide(directoryLayer), | 350 | Layer.provide(directoryLayer), |
| 348 | Layer.provide(defaultServerSettingsLayer), | 351 | Layer.provide(defaultServerSettingsLayer), |
| @@ -403,7 +406,7 @@ it.effect("ProviderServiceLive rejects new sessions for disabled providers", () · 54 unmodified lines | |||
| 403 | const directoryLayer = ProviderSessionDirectory.layer.pipe( | 406 | const directoryLayer = ProviderSessionDirectory.layer.pipe( |
| 404 | Layer.provide(runtimeRepositoryLayer), | 407 | Layer.provide(runtimeRepositoryLayer), |
| 405 | ); | 408 | ); |
| 406 | const providerLayer = makeProviderServiceLive().pipe( | 409 | const providerLayer = makeFreshProviderServiceLayer().pipe( |
| 407 | Layer.provide(providerAdapterLayer), | 410 | Layer.provide(providerAdapterLayer), |
| 408 | Layer.provide(directoryLayer), | 411 | Layer.provide(directoryLayer), |
| 409 | Layer.provide(defaultServerSettingsLayer), | 412 | Layer.provide(defaultServerSettingsLayer), |
| @@ -487,7 +490,7 @@ it.effect( · 77 unmodified lines | |||
| 487 | const directoryLayer = ProviderSessionDirectory.layer.pipe( | 490 | const directoryLayer = ProviderSessionDirectory.layer.pipe( |
| 488 | Layer.provide(runtimeRepositoryLayer), | 491 | Layer.provide(runtimeRepositoryLayer), |
| 489 | ); | 492 | ); |
| 490 | const providerLayer = makeProviderServiceLive().pipe( | 493 | const providerLayer = makeFreshProviderServiceLayer().pipe( |
| 491 | Layer.provide(providerAdapterLayer), | 494 | Layer.provide(providerAdapterLayer), |
| 492 | Layer.provide(directoryLayer), | 495 | Layer.provide(directoryLayer), |
| 493 | Layer.provide(serverSettingsLayer), | 496 | Layer.provide(serverSettingsLayer), |
| @@ -559,7 +562,7 @@ it.effect("ProviderServiceLive rejects new sessions for disabled custom instance · 65 unmodified lines | |||
| 559 | const directoryLayer = ProviderSessionDirectory.layer.pipe( | 562 | const directoryLayer = ProviderSessionDirectory.layer.pipe( |
| 560 | Layer.provide(runtimeRepositoryLayer), | 563 | Layer.provide(runtimeRepositoryLayer), |
| 561 | ); | 564 | ); |
| 562 | const providerLayer = makeProviderServiceLive().pipe( | 565 | const providerLayer = makeFreshProviderServiceLayer().pipe( |
| 563 | Layer.provide(providerAdapterLayer), | 566 | Layer.provide(providerAdapterLayer), |
| 564 | Layer.provide(directoryLayer), | 567 | Layer.provide(directoryLayer), |
| 565 | Layer.provide(defaultServerSettingsLayer), | 568 | Layer.provide(defaultServerSettingsLayer), |
| @@ -606,7 +609,7 @@ it.effect("ProviderServiceLive writes canonical events to the emitting thread se · 40 unmodified lines | |||
| 606 | const directoryLayer = ProviderSessionDirectory.layer.pipe( | 609 | const directoryLayer = ProviderSessionDirectory.layer.pipe( |
| 607 | Layer.provide(runtimeRepositoryLayer), | 610 | Layer.provide(runtimeRepositoryLayer), |
| 608 | ); | 611 | ); |
| 609 | const providerLayer = makeProviderServiceLive({ | 612 | const providerLayer = makeFreshProviderServiceLayer({ |
| 610 | canonicalEventLogger: { | 613 | canonicalEventLogger: { |
| 611 | filePath: "memory://provider-canonical-events", | 614 | filePath: "memory://provider-canonical-events", |
| 612 | write: (event, threadId) => { | 615 | write: (event, threadId) => { |
| @@ -678,7 +681,7 @@ it.effect("ProviderServiceLive keeps persisted resumable sessions on startup", ( · 65 unmodified lines | |||
| 678 | }); | 681 | }); |
| 679 | }).pipe(Effect.provide(directoryLayer)); | 682 | }).pipe(Effect.provide(directoryLayer)); |
| 680 | 683 | ||
| 681 | const providerLayer = makeProviderServiceLive().pipe( | 684 | const providerLayer = makeFreshProviderServiceLayer().pipe( |
| 682 | Layer.provide(Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, registry)), | 685 | Layer.provide(Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, registry)), |
| 683 | Layer.provide(directoryLayer), | 686 | Layer.provide(directoryLayer), |
| 684 | Layer.provide(defaultServerSettingsLayer), | 687 | Layer.provide(defaultServerSettingsLayer), |
| @@ -742,7 +745,7 @@ it.effect( · 57 unmodified lines | |||
| 742 | const firstDirectoryLayer = ProviderSessionDirectory.layer.pipe( | 745 | const firstDirectoryLayer = ProviderSessionDirectory.layer.pipe( |
| 743 | Layer.provide(runtimeRepositoryLayer), | 746 | Layer.provide(runtimeRepositoryLayer), |
| 744 | ); | 747 | ); |
| 745 | const firstProviderLayer = makeProviderServiceLive().pipe( | 748 | const firstProviderLayer = makeFreshProviderServiceLayer().pipe( |
| 746 | Layer.provide( | 749 | Layer.provide( |
| 747 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, firstRegistry), | 750 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, firstRegistry), |
| 748 | ), | 751 | ), |
| @@ -801,7 +804,7 @@ it.effect( · 52 unmodified lines | |||
| 801 | const secondDirectoryLayer = ProviderSessionDirectory.layer.pipe( | 804 | const secondDirectoryLayer = ProviderSessionDirectory.layer.pipe( |
| 802 | Layer.provide(runtimeRepositoryLayer), | 805 | Layer.provide(runtimeRepositoryLayer), |
| 803 | ); | 806 | ); |
| 804 | const secondProviderLayer = makeProviderServiceLive().pipe( | 807 | const secondProviderLayer = makeFreshProviderServiceLayer().pipe( |
| 805 | Layer.provide( | 808 | Layer.provide( |
| 806 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, secondRegistry), | 809 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, secondRegistry), |
| 807 | ), | 810 | ), |
| @@ -1312,7 +1315,7 @@ routing.layer("ProviderServiceLive routing", (it) => { · 504 unmodified lines | |||
| 1312 | const firstDirectoryLayer = ProviderSessionDirectory.layer.pipe( | 1315 | const firstDirectoryLayer = ProviderSessionDirectory.layer.pipe( |
| 1313 | Layer.provide(runtimeRepositoryLayer), | 1316 | Layer.provide(runtimeRepositoryLayer), |
| 1314 | ); | 1317 | ); |
| 1315 | const firstProviderLayer = makeProviderServiceLive().pipe( | 1318 | const firstProviderLayer = makeFreshProviderServiceLayer().pipe( |
| 1316 | Layer.provide( | 1319 | Layer.provide( |
| 1317 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, firstRegistry), | 1320 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, firstRegistry), |
| 1318 | ), | 1321 | ), |
| @@ -1350,7 +1353,7 @@ routing.layer("ProviderServiceLive routing", (it) => { · 31 unmodified lines | |||
| 1350 | const secondDirectoryLayer = ProviderSessionDirectory.layer.pipe( | 1353 | const secondDirectoryLayer = ProviderSessionDirectory.layer.pipe( |
| 1351 | Layer.provide(runtimeRepositoryLayer), | 1354 | Layer.provide(runtimeRepositoryLayer), |
| 1352 | ); | 1355 | ); |
| 1353 | const secondProviderLayer = makeProviderServiceLive().pipe( | 1356 | const secondProviderLayer = makeFreshProviderServiceLayer().pipe( |
| 1354 | Layer.provide( | 1357 | Layer.provide( |
| 1355 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, secondRegistry), | 1358 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, secondRegistry), |
| 1356 | ), | 1359 | ), |
| @@ -1418,7 +1421,7 @@ routing.layer("ProviderServiceLive routing", (it) => { · 61 unmodified lines | |||
| 1418 | const firstDirectoryLayer = ProviderSessionDirectory.layer.pipe( | 1421 | const firstDirectoryLayer = ProviderSessionDirectory.layer.pipe( |
| 1419 | Layer.provide(runtimeRepositoryLayer), | 1422 | Layer.provide(runtimeRepositoryLayer), |
| 1420 | ); | 1423 | ); |
| 1421 | const firstProviderLayer = makeProviderServiceLive().pipe( | 1424 | const firstProviderLayer = makeFreshProviderServiceLayer().pipe( |
| 1422 | Layer.provide( | 1425 | Layer.provide( |
| 1423 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, firstRegistry), | 1426 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, firstRegistry), |
| 1424 | ), | 1427 | ), |
| @@ -1451,7 +1454,7 @@ routing.layer("ProviderServiceLive routing", (it) => { · 26 unmodified lines | |||
| 1451 | const secondDirectoryLayer = ProviderSessionDirectory.layer.pipe( | 1454 | const secondDirectoryLayer = ProviderSessionDirectory.layer.pipe( |
| 1452 | Layer.provide(runtimeRepositoryLayer), | 1455 | Layer.provide(runtimeRepositoryLayer), |
| 1453 | ); | 1456 | ); |
| 1454 | const secondProviderLayer = makeProviderServiceLive().pipe( | 1457 | const secondProviderLayer = makeFreshProviderServiceLayer().pipe( |
| 1455 | Layer.provide( | 1458 | Layer.provide( |
| 1456 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, secondRegistry), | 1459 | Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistry, secondRegistry), |
| 1457 | ), | 1460 | ), |
| @@ -6,6 +6,8 @@ · 5 unmodified lines | |||
| 6 | * unified provider event stream for subscribers. | 6 | * unified provider event stream for subscribers. |
| 7 | * | 7 | * |
| 8 | * It does not implement provider protocol details (adapter concern). | 8 | * It does not implement provider protocol details (adapter concern). |
| 9 | * Uses Effect `Context.Service` for dependency injection and returns typed | ||
| 10 | * domain errors for validation, session, codex, and checkpoint workflows. | ||
| 9 | * | 11 | * |
| 10 | * @module ProviderService | 12 | * @module ProviderService |
| 11 | */ | 13 | */ |
| @@ -61,14 +63,22 @@ import * as ProviderSessionDirectory from "./ProviderSessionDirectory.ts"; · 49 unmodified lines | |||
| 61 | import { type EventNdjsonLogger } from "./Layers/EventNdjsonLogger.ts"; | 63 | import { type EventNdjsonLogger } from "./Layers/EventNdjsonLogger.ts"; |
| 62 | import * as ProviderEventLoggers from "./ProviderEventLoggers.ts"; | 64 | import * as ProviderEventLoggers from "./ProviderEventLoggers.ts"; |
| 63 | import * as AnalyticsService from "../telemetry/AnalyticsService.ts"; | 65 | import * as AnalyticsService from "../telemetry/AnalyticsService.ts"; |
| 64 | import * as McpProviderSession from "../mcp/McpProviderSession.ts"; | 66 | import { |
| 65 | import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts"; | 67 | clearAllMcpProviderSessions, |
| 68 | clearMcpProviderSession, | ||
| 69 | setMcpProviderSession, | ||
| 70 | } from "../mcp/McpProviderSession.ts"; | ||
| 71 | import { | ||
| 72 | issueActiveMcpCredential, | ||
| 73 | revokeActiveMcpThread, | ||
| 74 | revokeAllActiveMcpCredentials, | ||
| 75 | } from "../mcp/McpSessionRegistry.ts"; | ||
| 66 | const isModelSelection = Schema.is(ModelSelection); | 76 | const isModelSelection = Schema.is(ModelSelection); |
| 67 | 77 | ||
| 68 | export class ProviderService extends Context.Service< | 78 | export class ProviderService extends Context.Service< |
| 69 | ProviderService, | 79 | ProviderService, |
| 70 | { | 80 | { |
| 71 | /** Start a provider session for a thread. */ | 81 | /** Start a provider session. */ |
| 72 | readonly startSession: ( | 82 | readonly startSession: ( |
| 73 | threadId: ThreadId, | 83 | threadId: ThreadId, |
| 74 | input: ProviderSessionStartInput, | 84 | input: ProviderSessionStartInput, |
| @@ -99,10 +109,14 @@ export class ProviderService extends Context.Service< · 24 unmodified lines | |||
| 99 | input: ProviderStopSessionInput, | 109 | input: ProviderStopSessionInput, |
| 100 | ) => Effect.Effect<void, ProviderServiceError>; | 110 | ) => Effect.Effect<void, ProviderServiceError>; |
| 101 | 111 | ||
| 102 | /** Aggregate the active sessions reported by all registered adapters. */ | 112 | /** |
| 113 | * List active provider sessions. | ||
| 114 | * | ||
| 115 | * Aggregates runtime session lists from all registered adapters. | ||
| 116 | */ | ||
| 103 | readonly listSessions: () => Effect.Effect<ReadonlyArray<ProviderSession>>; | 117 | readonly listSessions: () => Effect.Effect<ReadonlyArray<ProviderSession>>; |
| 104 | 118 | ||
| 105 | /** Read capabilities for the adapter bound to an instance. */ | 119 | /** Read capabilities for the adapter bound to a configured provider instance. */ |
| 106 | readonly getCapabilities: ( | 120 | readonly getCapabilities: ( |
| 107 | instanceId: ProviderInstanceId, | 121 | instanceId: ProviderInstanceId, |
| 108 | ) => Effect.Effect<ProviderAdapterCapabilities, ProviderServiceError>; | 122 | ) => Effect.Effect<ProviderAdapterCapabilities, ProviderServiceError>; |
| @@ -119,8 +133,9 @@ export class ProviderService extends Context.Service< · 10 unmodified lines | |||
| 119 | }) => Effect.Effect<void, ProviderServiceError>; | 133 | }) => Effect.Effect<void, ProviderServiceError>; |
| 120 | 134 | ||
| 121 | /** | 135 | /** |
| 122 | * Canonical provider runtime event stream. ProviderService owns the | 136 | * Canonical provider runtime event stream. |
| 123 | * per-instance fan-out rather than delegating it to a separate event bus. | 137 | * |
| 138 | * Fan-out is owned by ProviderService (not by a standalone event-bus service). | ||
| 124 | */ | 139 | */ |
| 125 | readonly streamEvents: Stream.Stream<ProviderRuntimeEvent>; | 140 | readonly streamEvents: Stream.Stream<ProviderRuntimeEvent>; |
| 126 | } | 141 | } |
| @@ -279,16 +294,14 @@ export const make = Effect.fn("ProviderService.make")(function* (options?: Provi · 152 unmodified lines | |||
| 279 | const runtimeEventPubSub = yield* PubSub.unbounded<ProviderRuntimeEvent>(); | 294 | const runtimeEventPubSub = yield* PubSub.unbounded<ProviderRuntimeEvent>(); |
| 280 | const nowIso = Effect.map(DateTime.now, DateTime.formatIso); | 295 | const nowIso = Effect.map(DateTime.now, DateTime.formatIso); |
| 281 | const prepareMcpSession = (threadId: ThreadId, providerInstanceId: ProviderInstanceId) => | 296 | const prepareMcpSession = (threadId: ThreadId, providerInstanceId: ProviderInstanceId) => |
| 282 | McpSessionRegistry.issueActiveMcpCredential({ threadId, providerInstanceId }).pipe( | 297 | issueActiveMcpCredential({ threadId, providerInstanceId }).pipe( |
| 283 | Effect.tap((credential) => | 298 | Effect.tap((credential) => |
| 284 | credential | 299 | credential ? Effect.sync(() => setMcpProviderSession(credential.config)) : Effect.void, |
| 285 | ? Effect.sync(() => McpProviderSession.setMcpProviderSession(credential.config)) | ||
| 286 | : Effect.void, | ||
| 287 | ), | 300 | ), |
| 288 | ); | 301 | ); |
| 289 | const clearMcpSession = (threadId: ThreadId) => | 302 | const clearMcpSession = (threadId: ThreadId) => |
| 290 | McpSessionRegistry.revokeActiveMcpThread(threadId).pipe( | 303 | revokeActiveMcpThread(threadId).pipe( |
| 291 | Effect.tap(() => Effect.sync(() => McpProviderSession.clearMcpProviderSession(threadId))), | 304 | Effect.tap(() => Effect.sync(() => clearMcpProviderSession(threadId))), |
| 292 | ); | 305 | ); |
| 293 | 306 | ||
| 294 | const publishRuntimeEvent = (event: ProviderRuntimeEvent): Effect.Effect<void> => | 307 | const publishRuntimeEvent = (event: ProviderRuntimeEvent): Effect.Effect<void> => |
| @@ -1096,8 +1109,8 @@ export const make = Effect.fn("ProviderService.make")(function* (options?: Provi · 801 unmodified lines | |||
| 1096 | ), | 1109 | ), |
| 1097 | ).pipe(Effect.asVoid); | 1110 | ).pipe(Effect.asVoid); |
| 1098 | yield* Effect.forEach(currentAdapters, ([, adapter]) => adapter.stopAll()).pipe(Effect.asVoid); | 1111 | yield* Effect.forEach(currentAdapters, ([, adapter]) => adapter.stopAll()).pipe(Effect.asVoid); |
| 1099 | yield* McpSessionRegistry.revokeAllActiveMcpCredentials(); | 1112 | yield* revokeAllActiveMcpCredentials(); |
| 1100 | McpProviderSession.clearAllMcpProviderSessions(); | 1113 | clearAllMcpProviderSessions(); |
| 1101 | const bindings = yield* directory.listBindings().pipe(Effect.orElseSucceed(() => [])); | 1114 | const bindings = yield* directory.listBindings().pipe(Effect.orElseSucceed(() => [])); |
| 1102 | yield* Effect.forEach(bindings, (binding) => | 1115 | yield* Effect.forEach(bindings, (binding) => |
| 1103 | Effect.gen(function* () { | 1116 | Effect.gen(function* () { |
| @@ -20,9 +20,7 @@ import * as ProviderSessionRuntime from "../persistence/ProviderSessionRuntime.t · 19 unmodified lines | |||
| 20 | import * as ProviderSessionDirectory from "./ProviderSessionDirectory.ts"; | 20 | import * as ProviderSessionDirectory from "./ProviderSessionDirectory.ts"; |
| 21 | 21 | ||
| 22 | function makeDirectoryLayer<E, R>(persistenceLayer: Layer.Layer<SqlClient.SqlClient, E, R>) { | 22 | function makeDirectoryLayer<E, R>(persistenceLayer: Layer.Layer<SqlClient.SqlClient, E, R>) { |
| 23 | const runtimeRepositoryLayer = ProviderSessionRuntime.layer.pipe( | 23 | const runtimeRepositoryLayer = ProviderSessionRuntime.layer.pipe(Layer.provide(persistenceLayer)); |
| 24 | Layer.provide(persistenceLayer), | ||
| 25 | ); | ||
| 26 | return Layer.mergeAll( | 24 | return Layer.mergeAll( |
| 27 | runtimeRepositoryLayer, | 25 | runtimeRepositoryLayer, |
| 28 | ProviderSessionDirectory.layer.pipe(Layer.provide(runtimeRepositoryLayer)), | 26 | ProviderSessionDirectory.layer.pipe(Layer.provide(runtimeRepositoryLayer)), |
| @@ -14,7 +14,7 @@ import * as Option from "effect/Option"; · 13 unmodified lines | |||
| 14 | import * as Schema from "effect/Schema"; | 14 | import * as Schema from "effect/Schema"; |
| 15 | 15 | ||
| 16 | import { ProviderSessionDirectoryPersistenceError, ProviderValidationError } from "./Errors.ts"; | 16 | import { ProviderSessionDirectoryPersistenceError, ProviderValidationError } from "./Errors.ts"; |
| 17 | import * as ProviderSessionRuntime from "../persistence/Services/ProviderSessionRuntime.ts"; | 17 | import * as ProviderSessionRuntime from "../persistence/ProviderSessionRuntime.ts"; |
| 18 | 18 | ||
| 19 | export interface ProviderRuntimeBinding { | 19 | export interface ProviderRuntimeBinding { |
| 20 | readonly threadId: ThreadId; | 20 | readonly threadId: ThreadId; |
| @@ -119,6 +119,9 @@ function toRuntimeBinding( · 98 unmodified lines | |||
| 119 | ({ | 119 | ({ |
| 120 | threadId: runtime.threadId, | 120 | threadId: runtime.threadId, |
| 121 | provider, | 121 | provider, |
| 122 | // Migration boundary: rows written before provider instances had a | ||
| 123 | // nullable id. Promote them here so runtime routing never has to | ||
| 124 | // infer an instance from its driver kind. | ||
| 122 | providerInstanceId: runtime.providerInstanceId ?? defaultInstanceIdForDriver(provider), | 125 | providerInstanceId: runtime.providerInstanceId ?? defaultInstanceIdForDriver(provider), |
| 123 | adapterKey: runtime.adapterKey, | 126 | adapterKey: runtime.adapterKey, |
| 124 | runtimeMode: runtime.runtimeMode, | 127 | runtimeMode: runtime.runtimeMode, |
| @@ -14,6 +14,7 @@ import * as ProviderSessionDirectory from "./ProviderSessionDirectory.ts"; · 13 unmodified lines | |||
| 14 | export class ProviderSessionReaper extends Context.Service< | 14 | export class ProviderSessionReaper extends Context.Service< |
| 15 | ProviderSessionReaper, | 15 | ProviderSessionReaper, |
| 16 | { | 16 | { |
| 17 | /** Start the background provider session reaper within the provided scope. */ | ||
| 17 | readonly start: () => Effect.Effect<void, never, Scope.Scope>; | 18 | readonly start: () => Effect.Effect<void, never, Scope.Scope>; |
| 18 | } | 19 | } |
| 19 | >()("t3/provider/ProviderSessionReaper") {} | 20 | >()("t3/provider/ProviderSessionReaper") {} |
| @@ -1,3 +0,0 @@ | |||
| 1 | // Compatibility shim for the intentionally excluded orchestration harness. | ||
| 2 | export { ProviderAdapterRegistry } from "../ProviderAdapterRegistry.ts"; | ||
| 3 | export type { ProviderInstanceRoutingInfo } from "../ProviderAdapterRegistry.ts"; | ||
| @@ -1,2 +0,0 @@ | |||
| 1 | // Compatibility shim for the intentionally excluded orchestration modules. | ||
| 2 | export * from "../ProviderRegistry.ts"; | ||
| @@ -1,5 +0,0 @@ | |||
| 1 | // Compatibility shim for the intentionally excluded orchestration modules and harness. | ||
| 2 | export * from "../ProviderService.ts"; | ||
| 3 | |||
| 4 | /** @deprecated Use `ProviderService["Service"]` from the canonical module. */ | ||
| 5 | export type ProviderServiceShape = import("../ProviderService.ts").ProviderService["Service"]; | ||
| @@ -26,7 +26,7 @@ import * as Schema from "effect/Schema"; · 25 unmodified lines | |||
| 26 | import * as Stream from "effect/Stream"; | 26 | import * as Stream from "effect/Stream"; |
| 27 | import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; | 27 | import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; |
| 28 | 28 | ||
| 29 | import * as ProcessRunner from "../processRunner.ts"; | 29 | import { isWindowsCommandNotFound } from "../processRunner.ts"; |
| 30 | import { collectStreamAsString } from "./providerSnapshot.ts"; | 30 | import { collectStreamAsString } from "./providerSnapshot.ts"; |
| 31 | import * as NetService from "@t3tools/shared/Net"; | 31 | import * as NetService from "@t3tools/shared/Net"; |
| 32 | import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; | 32 | import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; |
| @@ -54,12 +54,15 @@ export class OpenCodeRuntimeError extends Schema.TaggedErrorClass<OpenCodeRuntim · 21 unmodified lines | |||
| 54 | operation: Schema.String, | 54 | operation: Schema.String, |
| 55 | detail: Schema.String, | 55 | detail: Schema.String, |
| 56 | cause: Schema.optional(Schema.Defect()), | 56 | cause: Schema.optional(Schema.Defect()), |
| 57 | exitCode: Schema.optionalKey(Schema.Number), | ||
| 58 | stdout: Schema.optionalKey(Schema.String), | ||
| 59 | stderr: Schema.optionalKey(Schema.String), | ||
| 57 | }, | 60 | }, |
| 58 | ) { | 61 | ) { |
| 59 | static readonly is = Schema.is(OpenCodeRuntimeError); | 62 | static readonly is = Schema.is(OpenCodeRuntimeError); |
| 60 | 63 | ||
| 61 | override get message(): string { | 64 | override get message(): string { |
| 62 | return `${this.operation}: ${this.detail}`; | 65 | return `OpenCode runtime operation ${this.operation} failed.`; |
| 63 | } | 66 | } |
| 64 | } | 67 | } |
| 65 | 68 | ||
| @@ -302,7 +305,7 @@ export const make = Effect.gen(function* () { · 236 unmodified lines | |||
| 302 | { concurrency: "unbounded" }, | 305 | { concurrency: "unbounded" }, |
| 303 | ); | 306 | ); |
| 304 | const exitCode = Number(code); | 307 | const exitCode = Number(code); |
| 305 | if (yield* ProcessRunner.isWindowsCommandNotFound(exitCode, stderr)) { | 308 | if (yield* isWindowsCommandNotFound(exitCode, stderr)) { |
| 306 | return yield* new OpenCodeRuntimeError({ | 309 | return yield* new OpenCodeRuntimeError({ |
| 307 | operation: "runOpenCodeCommand", | 310 | operation: "runOpenCodeCommand", |
| 308 | detail: `spawn ${input.binaryPath} ENOENT`, | 311 | detail: `spawn ${input.binaryPath} ENOENT`, |
| @@ -437,7 +440,9 @@ export const make = Effect.gen(function* () { · 128 unmodified lines | |||
| 437 | ] | 440 | ] |
| 438 | .filter(Boolean) | 441 | .filter(Boolean) |
| 439 | .join("\n\n"), | 442 | .join("\n\n"), |
| 440 | cause: { exitCode, stdout, stderr }, | 443 | exitCode, |
| 444 | stdout, | ||
| 445 | stderr, | ||
| 441 | }), | 446 | }), |
| 442 | ).pipe(Effect.ignore); | 447 | ).pipe(Effect.ignore); |
| 443 | }), | 448 | }), |
| @@ -11,6 +11,7 @@ import * as Effect from "effect/Effect"; · 10 unmodified lines | |||
| 11 | import * as Exit from "effect/Exit"; | 11 | import * as Exit from "effect/Exit"; |
| 12 | import * as Fiber from "effect/Fiber"; | 12 | import * as Fiber from "effect/Fiber"; |
| 13 | import * as Layer from "effect/Layer"; | 13 | import * as Layer from "effect/Layer"; |
| 14 | import * as PlatformError from "effect/PlatformError"; | ||
| 14 | import * as Ref from "effect/Ref"; | 15 | import * as Ref from "effect/Ref"; |
| 15 | import * as Schema from "effect/Schema"; | 16 | import * as Schema from "effect/Schema"; |
| 16 | import * as Sink from "effect/Sink"; | 17 | import * as Sink from "effect/Sink"; |
| @@ -140,6 +141,13 @@ function mockSpawnerLayer( · 123 unmodified lines | |||
| 140 | ); | 141 | ); |
| 141 | } | 142 | } |
| 142 | 143 | ||
| 144 | function failingSpawnerLayer(cause: PlatformError.PlatformError) { | ||
| 145 | return Layer.succeed( | ||
| 146 | ChildProcessSpawner.ChildProcessSpawner, | ||
| 147 | ChildProcessSpawner.make(() => Effect.fail(cause)), | ||
| 148 | ); | ||
| 149 | } | ||
| 150 | |||
| 143 | function makeRegistry( | 151 | function makeRegistry( |
| 144 | initialProviders: ServerProvider | ReadonlyArray<ServerProvider> = baseProvider, | 152 | initialProviders: ServerProvider | ReadonlyArray<ServerProvider> = baseProvider, |
| 145 | ) { | 153 | ) { |
| @@ -320,6 +328,29 @@ describe("providerMaintenanceRunner", () => { · 174 unmodified lines | |||
| 320 | }, | 328 | }, |
| 321 | ); | 329 | ); |
| 322 | 330 | ||
| 331 | it.effect("preserves update spawn failure messages", () => { | ||
| 332 | const cause = PlatformError.systemError({ | ||
| 333 | _tag: "PermissionDenied", | ||
| 334 | module: "ChildProcess", | ||
| 335 | method: "spawn", | ||
| 336 | pathOrDescriptor: "npm", | ||
| 337 | description: "", | ||
| 338 | }); | ||
| 339 | return Effect.gen(function* () { | ||
| 340 | const { registry } = yield* makeRegistry(baseProvider); | ||
| 341 | const runner = yield* makeTestRunner(registry); | ||
| 342 | |||
| 343 | const result = yield* runner.updateProvider(CODEX_DRIVER); | ||
| 344 | assert.strictEqual(result.providers[0]?.updateState?.status, "failed"); | ||
| 345 | assert.strictEqual( | ||
| 346 | result.providers[0]?.updateState?.message, | ||
| 347 | `Failed to run update command npm: ${cause.message}`, | ||
| 348 | ); | ||
| 349 | }).pipe( | ||
| 350 | Effect.provide(Layer.mergeAll(latestVersionHttpClient("0.0.0"), failingSpawnerLayer(cause))), | ||
| 351 | ); | ||
| 352 | }); | ||
| 353 | |||
| 323 | it.effect("updates a single provider instance without touching sibling instances", () => { | 354 | it.effect("updates a single provider instance without touching sibling instances", () => { |
| 324 | const calls: Array<{ command: string; args: ReadonlyArray<string> }> = []; | 355 | const calls: Array<{ command: string; args: ReadonlyArray<string> }> = []; |
| 325 | return Effect.gen(function* () { | 356 | return Effect.gen(function* () { |
| @@ -14,6 +14,7 @@ import * as Duration from "effect/Duration"; · 13 unmodified lines | |||
| 14 | import * as Effect from "effect/Effect"; | 14 | import * as Effect from "effect/Effect"; |
| 15 | import * as Layer from "effect/Layer"; | 15 | import * as Layer from "effect/Layer"; |
| 16 | import * as Option from "effect/Option"; | 16 | import * as Option from "effect/Option"; |
| 17 | import * as PlatformError from "effect/PlatformError"; | ||
| 17 | import * as Ref from "effect/Ref"; | 18 | import * as Ref from "effect/Ref"; |
| 18 | import * as Schema from "effect/Schema"; | 19 | import * as Schema from "effect/Schema"; |
| 19 | import { HttpClient } from "effect/unstable/http"; | 20 | import { HttpClient } from "effect/unstable/http"; |
| @@ -52,20 +53,68 @@ export class ProviderMaintenanceRunner extends Context.Service< · 32 unmodified lines | |||
| 52 | } | 53 | } |
| 53 | >()("t3/provider/providerMaintenanceRunner") {} | 54 | >()("t3/provider/providerMaintenanceRunner") {} |
| 54 | 55 | ||
| 55 | class ProviderMaintenanceCommandError extends Schema.TaggedErrorClass<ProviderMaintenanceCommandError>()( | 56 | const platformFailureFields = { |
| 56 | "ProviderMaintenanceCommandError", | 57 | failureTag: Schema.NullOr(Schema.String), |
| 58 | failureModule: Schema.String, | ||
| 59 | failureMethod: Schema.String, | ||
| 60 | failureDescription: Schema.NullOr(Schema.String), | ||
| 61 | failurePathOrDescriptor: Schema.NullOr(Schema.Union([Schema.String, Schema.Number])), | ||
| 62 | } as const; | ||
| 63 | |||
| 64 | interface PlatformFailureAttributes { | ||
| 65 | readonly failureTag: string | null; | ||
| 66 | readonly failureModule: string; | ||
| 67 | readonly failureMethod: string; | ||
| 68 | readonly failureDescription: string | null; | ||
| 69 | readonly failurePathOrDescriptor: string | number | null; | ||
| 70 | } | ||
| 71 | |||
| 72 | function platformFailureAttributes(cause: PlatformError.PlatformError): PlatformFailureAttributes { | ||
| 73 | const { reason } = cause; | ||
| 74 | return { | ||
| 75 | failureTag: reason._tag === "BadArgument" ? null : reason._tag, | ||
| 76 | failureModule: reason.module, | ||
| 77 | failureMethod: reason.method, | ||
| 78 | failureDescription: reason.description || null, | ||
| 79 | failurePathOrDescriptor: | ||
| 80 | "pathOrDescriptor" in reason ? (reason.pathOrDescriptor ?? null) : null, | ||
| 81 | }; | ||
| 82 | } | ||
| 83 | |||
| 84 | function formatPlatformFailure(attributes: PlatformFailureAttributes): string { | ||
| 85 | const tag = attributes.failureTag === null ? "" : `${attributes.failureTag}: `; | ||
| 86 | const path = | ||
| 87 | attributes.failurePathOrDescriptor === null | ||
| 88 | ? "" | ||
| 89 | : ` (${String(attributes.failurePathOrDescriptor)})`; | ||
| 90 | const description = | ||
| 91 | attributes.failureDescription === null ? "" : `: ${attributes.failureDescription}`; | ||
| 92 | return `${tag}${attributes.failureModule}.${attributes.failureMethod}${path}${description}`; | ||
| 93 | } | ||
| 94 | |||
| 95 | class ProviderMaintenanceCommandSpawnError extends Schema.TaggedErrorClass<ProviderMaintenanceCommandSpawnError>()( | ||
| 96 | "ProviderMaintenanceCommandSpawnError", | ||
| 57 | { | 97 | { |
| 58 | operation: Schema.Literals(["spawn", "collect"]), | 98 | command: Schema.String, |
| 59 | command: Schema.optional(Schema.String), | 99 | ...platformFailureFields, |
| 60 | cause: Schema.Defect(), | 100 | cause: Schema.Defect(), |
| 61 | }, | 101 | }, |
| 62 | ) { | 102 | ) { |
| 63 | override get message(): string { | 103 | override get message(): string { |
| 64 | const causeMessage = this.cause instanceof Error ? this.cause.message : undefined; | 104 | return `Failed to run update command ${this.command}: ${formatPlatformFailure(this)}`; |
| 65 | if (this.operation === "spawn") { | 105 | } |
| 66 | return `Failed to run update command ${this.command ?? "unknown"}${causeMessage ? `: ${causeMessage}` : "."}`; | 106 | } |
| 67 | } | 107 | |
| 68 | return causeMessage ?? "Update command failed to run."; | 108 | class ProviderMaintenanceCommandCollectError extends Schema.TaggedErrorClass<ProviderMaintenanceCommandCollectError>()( |
| 109 | "ProviderMaintenanceCommandCollectError", | ||
| 110 | { | ||
| 111 | command: Schema.String, | ||
| 112 | ...platformFailureFields, | ||
| 113 | cause: Schema.Defect(), | ||
| 114 | }, | ||
| 115 | ) { | ||
| 116 | override get message(): string { | ||
| 117 | return formatPlatformFailure(this); | ||
| 69 | } | 118 | } |
| 70 | } | 119 | } |
| 71 | 120 | ||
| @@ -89,9 +138,9 @@ const runProviderMaintenanceCommandWithSpawner = Effect.fn("ProviderMaintenanceR · 17 unmodified lines | |||
| 89 | .pipe( | 138 | .pipe( |
| 90 | Effect.mapError( | 139 | Effect.mapError( |
| 91 | (cause) => | 140 | (cause) => |
| 92 | new ProviderMaintenanceCommandError({ | 141 | new ProviderMaintenanceCommandSpawnError({ |
| 93 | operation: "spawn", | ||
| 94 | command: input.command, | 142 | command: input.command, |
| 143 | ...platformFailureAttributes(cause), | ||
| 95 | cause, | 144 | cause, |
| 96 | }), | 145 | }), |
| 97 | ), | 146 | ), |
| @@ -114,8 +163,9 @@ const runProviderMaintenanceCommandWithSpawner = Effect.fn("ProviderMaintenanceR · 16 unmodified lines | |||
| 114 | ).pipe( | 163 | ).pipe( |
| 115 | Effect.mapError( | 164 | Effect.mapError( |
| 116 | (cause) => | 165 | (cause) => |
| 117 | new ProviderMaintenanceCommandError({ | 166 | new ProviderMaintenanceCommandCollectError({ |
| 118 | operation: "collect", | 167 | command: input.command, |
| 168 | ...platformFailureAttributes(cause), | ||
| 119 | cause, | 169 | cause, |
| 120 | }), | 170 | }), |
| 121 | ), | 171 | ), |
| @@ -132,7 +132,7 @@ sequenceDiagram · 131 unmodified lines | |||
| 132 | [5]: ../apps/server/src/wsServer/pushBus.ts | 132 | [5]: ../apps/server/src/wsServer/pushBus.ts |
| 133 | [6]: ../packages/contracts/src/ws.ts | 133 | [6]: ../packages/contracts/src/ws.ts |
| 134 | [7]: ../apps/server/src/serverLayers.ts | 134 | [7]: ../apps/server/src/serverLayers.ts |
| 135 | [8]: ../apps/server/src/provider/Layers/ProviderService.ts | 135 | [8]: ../apps/server/src/provider/ProviderService.ts |
| 136 | [9]: ../apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts | 136 | [9]: ../apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts |
| 137 | [10]: ../apps/server/src/orchestration/Layers/OrchestrationEngine.ts | 137 | [10]: ../apps/server/src/orchestration/Layers/OrchestrationEngine.ts |
| 138 | [11]: ../packages/contracts/src/orchestration.ts | 138 | [11]: ../packages/contracts/src/orchestration.ts |
| @@ -41,7 +41,7 @@ Effect.fn("name")( · 40 unmodified lines | |||
| 41 | 41 | ||
| 42 | ## Suggested Order | 42 | ## Suggested Order |
| 43 | 43 | ||
| 44 | - [ ] `apps/server/src/provider/Layers/ProviderService.ts` | 44 | - [ ] `apps/server/src/provider/ProviderService.ts` |
| 45 | - [x] `apps/server/src/provider/Layers/ClaudeAdapter.ts` | 45 | - [x] `apps/server/src/provider/Layers/ClaudeAdapter.ts` |
| 46 | - [x] `apps/server/src/provider/Layers/CodexAdapter.ts` | 46 | - [x] `apps/server/src/provider/Layers/CodexAdapter.ts` |
| 47 | - [x] `apps/server/src/git/Layers/GitCore.ts` | 47 | - [x] `apps/server/src/git/Layers/GitCore.ts` |
| @@ -98,20 +98,20 @@ Effect.fn("name")( · 50 unmodified lines | |||
| 98 | - [x] `Effect.forEach(..., entry => Effect.gen(...))` callbacks around `L305` | 98 | - [x] `Effect.forEach(..., entry => Effect.gen(...))` callbacks around `L305` |
| 99 | - [x] Remaining apply helpers in this file | 99 | - [x] Remaining apply helpers in this file |
| 100 | 100 | ||
| 101 | ### `apps/server/src/provider/Layers/ProviderService.ts` (`24`) | 101 | ### `apps/server/src/provider/ProviderService.ts` (`24`) |
| 102 | 102 | ||
| 103 | - [ ] [makeProviderService](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ProviderService.ts#L134) | 103 | - [ ] [makeProviderService](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/ProviderService.ts#L134) |
| 104 | - [ ] [recoverSessionForThread](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ProviderService.ts#L196) | 104 | - [ ] [recoverSessionForThread](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/ProviderService.ts#L196) |
| 105 | - [ ] [resolveRoutableSession](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ProviderService.ts#L255) | 105 | - [ ] [resolveRoutableSession](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/ProviderService.ts#L255) |
| 106 | - [ ] [startSession](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ProviderService.ts#L284) | 106 | - [ ] [startSession](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/ProviderService.ts#L284) |
| 107 | - [ ] [sendTurn](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ProviderService.ts#L347) | 107 | - [ ] [sendTurn](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/ProviderService.ts#L347) |
| 108 | - [ ] [interruptTurn](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ProviderService.ts#L393) | 108 | - [ ] [interruptTurn](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/ProviderService.ts#L393) |
| 109 | - [ ] [respondToRequest](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ProviderService.ts#L411) | 109 | - [ ] [respondToRequest](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/ProviderService.ts#L411) |
| 110 | - [ ] [respondToUserInput](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ProviderService.ts#L430) | 110 | - [ ] [respondToUserInput](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/ProviderService.ts#L430) |
| 111 | - [ ] [stopSession](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ProviderService.ts#L445) | 111 | - [ ] [stopSession](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/ProviderService.ts#L445) |
| 112 | - [ ] [listSessions](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ProviderService.ts#L466) | 112 | - [ ] [listSessions](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/ProviderService.ts#L466) |
| 113 | - [ ] [rollbackConversation](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ProviderService.ts#L516) | 113 | - [ ] [rollbackConversation](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/ProviderService.ts#L516) |
| 114 | - [ ] [runStopAll](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ProviderService.ts#L538) | 114 | - [ ] [runStopAll](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/ProviderService.ts#L538) |
| 115 | 115 | ||
| 116 | ### `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts` (`14`) | 116 | ### `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts` (`14`) |
| 117 | 117 | ||
| @@ -174,7 +174,7 @@ Effect.fn("name")( · 56 unmodified lines | |||
| 174 | - [ ] [packages/shared/src/DrainableWorker.ts](/Users/julius/Development/Work/codething-mvp/packages/shared/src/DrainableWorker.ts) (`4`) | 174 | - [ ] [packages/shared/src/DrainableWorker.ts](/Users/julius/Development/Work/codething-mvp/packages/shared/src/DrainableWorker.ts) (`4`) |
| 175 | - [ ] [apps/server/src/wsServer/pushBus.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/wsServer/pushBus.ts) (`4`) | 175 | - [ ] [apps/server/src/wsServer/pushBus.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/wsServer/pushBus.ts) (`4`) |
| 176 | - [ ] [apps/server/src/wsServer.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/wsServer.ts) (`4`) | 176 | - [ ] [apps/server/src/wsServer.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/wsServer.ts) (`4`) |
| 177 | - [ ] [apps/server/src/provider/Layers/ProviderRegistry.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ProviderRegistry.ts) (`4`) | 177 | - [ ] [apps/server/src/provider/ProviderRegistry.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/ProviderRegistry.ts) (`4`) |
| 178 | - [ ] [apps/server/src/persistence/Layers/Sqlite.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/persistence/Layers/Sqlite.ts) (`4`) | 178 | - [ ] [apps/server/src/persistence/Layers/Sqlite.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/persistence/Layers/Sqlite.ts) (`4`) |
| 179 | - [ ] [apps/server/src/orchestration/Layers/ProviderCommandReactor.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts) (`4`) | 179 | - [ ] [apps/server/src/orchestration/Layers/ProviderCommandReactor.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts) (`4`) |
| 180 | - [ ] [apps/server/src/main.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/main.ts) (`4`) | 180 | - [ ] [apps/server/src/main.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/main.ts) (`4`) |
| @@ -183,7 +183,7 @@ Effect.fn("name")( · 2 unmodified lines | |||
| 183 | - [ ] [apps/server/src/serverLayers.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/serverLayers.ts) (`3`) | 183 | - [ ] [apps/server/src/serverLayers.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/serverLayers.ts) (`3`) |
| 184 | - [ ] [apps/server/src/telemetry/Layers/AnalyticsService.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/telemetry/Layers/AnalyticsService.ts) (`2`) | 184 | - [ ] [apps/server/src/telemetry/Layers/AnalyticsService.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/telemetry/Layers/AnalyticsService.ts) (`2`) |
| 185 | - [ ] [apps/server/src/telemetry/Identify.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/telemetry/Identify.ts) (`2`) | 185 | - [ ] [apps/server/src/telemetry/Identify.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/telemetry/Identify.ts) (`2`) |
| 186 | - [ ] [apps/server/src/provider/Layers/ProviderAdapterRegistry.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ProviderAdapterRegistry.ts) (`2`) | 186 | - [ ] [apps/server/src/provider/ProviderAdapterRegistry.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/ProviderAdapterRegistry.ts) (`2`) |
| 187 | - [ ] [apps/server/src/provider/Layers/CodexProvider.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/CodexProvider.ts) (`2`) | 187 | - [ ] [apps/server/src/provider/Layers/CodexProvider.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/CodexProvider.ts) (`2`) |
| 188 | - [ ] [apps/server/src/provider/Layers/ClaudeProvider.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ClaudeProvider.ts) (`2`) | 188 | - [ ] [apps/server/src/provider/Layers/ClaudeProvider.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/provider/Layers/ClaudeProvider.ts) (`2`) |
| 189 | - [ ] [apps/server/src/persistence/NodeSqliteClient.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/persistence/NodeSqliteClient.ts) (`2`) | 189 | - [ ] [apps/server/src/persistence/NodeSqliteClient.ts](/Users/julius/Development/Work/codething-mvp/apps/server/src/persistence/NodeSqliteClient.ts) (`2`) |
| @@ -167,7 +167,7 @@ The file patch and changed-file summary for one turn. It is usually computed in · 166 unmodified lines | |||
| 167 | [11]: ../apps/server/src/orchestration/Layers/ProjectionPipeline.ts | 167 | [11]: ../apps/server/src/orchestration/Layers/ProjectionPipeline.ts |
| 168 | [12]: ../apps/server/src/orchestration/Layers/ProviderCommandReactor.ts | 168 | [12]: ../apps/server/src/orchestration/Layers/ProviderCommandReactor.ts |
| 169 | [13]: ../apps/server/src/orchestration/Services/RuntimeReceiptBus.ts | 169 | [13]: ../apps/server/src/orchestration/Services/RuntimeReceiptBus.ts |
| 170 | [14]: ../apps/server/src/provider/Layers/ProviderService.ts | 170 | [14]: ../apps/server/src/provider/ProviderService.ts |
| 171 | [15]: ../apps/server/src/provider/Services/ProviderAdapter.ts | 171 | [15]: ../apps/server/src/provider/Services/ProviderAdapter.ts |
| 172 | [16]: ./provider-architecture.md | 172 | [16]: ./provider-architecture.md |
| 173 | [17]: ../apps/server/src/provider/Layers/CodexAdapter.ts | 173 | [17]: ../apps/server/src/provider/Layers/CodexAdapter.ts |