From 79b8fd7b0096c3a75e69e877df1d5253daa7e1f8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 18:49:09 +0000 Subject: [PATCH] Fix ProviderCommandReactor: add error recovery and deduplicate CWD resolution - Wrap processDomainEvent in catchCause to prevent the reactor fiber from terminating on handler errors, matching the pattern used by ProviderRuntimeIngestion and CheckpointReactor. - Replace duplicated resolveEffectiveThreadCwd with the shared resolveThreadWorkspaceCwd from checkpointing/Utils.ts. --- .../Layers/ProviderCommandReactor.ts | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts b/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts index d3ec9b676b636a50984f4d54cb52dcb358883e84..3b7e73fea9c144646321dfbecc44568a359a1f8d 100644 --- a/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts +++ b/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts @@ -8,8 +8,9 @@ import { type ThreadId, type TurnId, } from "@t3tools/contracts"; -import { Cache, Duration, Effect, Layer, Option, Queue, Stream } from "effect"; +import { Cache, Cause, Duration, Effect, Layer, Option, Queue, Stream } from "effect"; +import { resolveThreadWorkspaceCwd } from "../../checkpointing/Utils.ts"; import { ProviderService } from "../../provider/Services/ProviderService.ts"; import { OrchestrationEngineService } from "../Services/OrchestrationEngine.ts"; import { @@ -33,24 +34,6 @@ function toNonEmptyProviderInput(value: string | undefined): string | undefined return normalized && normalized.length > 0 ? normalized : undefined; } -function resolveEffectiveThreadCwd(input: { - readonly thread: { - readonly projectId: string; - readonly worktreePath: string | null; - }; - readonly projects: ReadonlyArray<{ - readonly id: string; - readonly workspaceRoot: string; - }>; -}): string | undefined { - const worktreeCwd = input.thread.worktreePath ?? undefined; - if (worktreeCwd) { - return worktreeCwd; - } - - return input.projects.find((project) => project.id === input.thread.projectId)?.workspaceRoot; -} - function mapProviderSessionStatusToOrchestrationStatus( status: "connecting" | "ready" | "running" | "error" | "closed", ): OrchestrationSession["status"] { @@ -161,7 +144,7 @@ const make = Effect.gen(function* () { thread.session?.providerName === "codex" || thread.session?.providerName === "claudeCode" ? thread.session.providerName : undefined; - const effectiveCwd = resolveEffectiveThreadCwd({ + const effectiveCwd = resolveThreadWorkspaceCwd({ thread, projects: readModel.projects, }); @@ -353,12 +336,25 @@ const make = Effect.gen(function* () { } }); + const processDomainEventSafely = (event: ProviderIntentEvent) => + processDomainEvent(event).pipe( + Effect.catchCause((cause) => { + if (Cause.hasInterruptsOnly(cause)) { + return Effect.failCause(cause); + } + return Effect.logWarning("provider command reactor failed to process event", { + eventType: event.type, + cause: Cause.pretty(cause), + }); + }), + ); + const start: ProviderCommandReactorShape["start"] = Effect.gen(function* () { const queue = yield* Queue.unbounded(); yield* Effect.addFinalizer(() => Queue.shutdown(queue).pipe(Effect.asVoid)); yield* Effect.forkScoped( - Effect.forever(Queue.take(queue).pipe(Effect.flatMap(processDomainEvent))), + Effect.forever(Queue.take(queue).pipe(Effect.flatMap(processDomainEventSafely))), ); yield* Effect.forkScoped( -- 2.54.0