import { ProviderInstanceId, RunId, ThreadId, TurnItemId, type OrchestrationV2ThreadProjection, } from "@t3tools/contracts"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import { expect, it } from "vite-plus/test"; import * as EffectWorker from "./EffectWorker.ts"; import * as EffectOutbox from "./EffectOutbox.ts"; import * as EventSink from "./EventSink.ts"; import * as IdAllocator from "./IdAllocator.ts"; import * as ProjectionStore from "./ProjectionStore.ts"; import * as ProviderRuntimeRecovery from "./ProviderRuntimeRecoveryService.ts"; import * as ServerSettings from "../serverSettings.ts"; it("uses the thread provider for stale background work without provider threads", async () => { const threadId = ThreadId.make("thread_recovery_background_no_provider_threads"); const runId = RunId.make("run_recovery_background_no_provider_threads"); const itemId = TurnItemId.make("turn_item_recovery_background_no_provider_threads"); const providerInstanceId = ProviderInstanceId.make("codex"); let committedInput: Parameters[0] | null = null; const projection = { thread: { id: threadId, providerInstanceId }, runtimeRequests: [], providerSessions: [], providerThreads: [], providerTurns: [], runs: [{ id: runId, status: "completed", providerInstanceId }], attempts: [], nodes: [], subagents: [], messages: [], turnItems: [ { id: itemId, runId: null, nodeId: null, providerThreadId: null, type: "command_execution", status: "running", }, ], } as unknown as OrchestrationV2ThreadProjection; const layer = ProviderRuntimeRecovery.layer.pipe( Layer.provide(ServerSettings.layerTest()), Layer.provide( Layer.mergeAll( Layer.mock(ProjectionStore.ProjectionStoreV2)({ getRecoveryThreadIds: () => Effect.succeed([threadId]), getThreadProjection: () => Effect.succeed(projection), }), Layer.mock(EventSink.EventSinkV2)({ commitCommand: (input) => { committedInput = input; return Effect.succeed({ committed: true, cancelledEffectCount: 0 } as never); }, }), IdAllocator.layer, Layer.mock(EffectWorker.OrchestrationEffectWorkerV2)({ runRecoveryOnce: Effect.succeed(false), }), Layer.mock(EffectOutbox.EffectOutboxV2)({ reconcileAfterProcessLoss: Effect.succeed({ requeued: 0, cancelled: 0 }), }), ), ), ); await Effect.gen(function* () { yield* (yield* ProviderRuntimeRecovery.ProviderRuntimeRecoveryService).reconcile("startup"); const event = committedInput?.events.find( (candidate) => candidate.type === "turn-item.updated", ); expect(event?.providerInstanceId).toBe(providerInstanceId); expect(event?.type === "turn-item.updated" ? event.payload.status : null).toBe("cancelled"); }).pipe(Effect.provide(layer), Effect.runPromise); });