import * as NodeCrypto from "@effect/platform-node/NodeCrypto"; import { expect, it } from "@effect/vitest"; import { EnvironmentId, ProviderInstanceId, ThreadId } from "@t3tools/contracts"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import { OrchestratorProjectionError } from "../orchestration-v2/Orchestrator.ts"; import * as ThreadManagement from "../orchestration-v2/ThreadManagementService.ts"; import type * as McpInvocationContext from "./McpInvocationContext.ts"; import * as ThreadMetadataMcp from "./ThreadMetadataMcpService.ts"; const threadId = ThreadId.make("thread:metadata-caller"); const scope: McpInvocationContext.McpInvocationScope = { environmentId: EnvironmentId.make("environment:metadata-test"), threadId, providerSessionId: "provider-session:metadata-test", providerInstanceId: ProviderInstanceId.make("codex"), capabilities: new Set(["orchestration"]), issuedAt: 1, }; function serviceLayer( getThreadShell: ThreadManagement.ThreadManagementService["Service"]["getThreadShell"], ) { return ThreadMetadataMcp.layer.pipe( Layer.provide( Layer.merge( Layer.mock(ThreadManagement.ThreadManagementService)({ getThreadShell, getThreadProjection: () => Effect.die("projection must not load after shell failure"), } satisfies Partial), NodeCrypto.layer, ), ), ); } const updateCallingThread = Effect.gen(function* () { const service = yield* ThreadMetadataMcp.ThreadMetadataMcpService; return yield* service.update(scope, { action: "rename", title: "Renamed thread", clientRequestId: "metadata-caller-classification", }); }); it.effect("reports an absent calling thread as thread_not_found", () => Effect.gen(function* () { const error = yield* updateCallingThread.pipe( Effect.provide(serviceLayer(() => Effect.succeed(null))), Effect.flip, ); expect(error.code).toBe("thread_not_found"); }), ); it.effect("keeps calling-thread storage failures as orchestration errors", () => Effect.gen(function* () { const error = yield* updateCallingThread.pipe( Effect.provide( serviceLayer(() => Effect.fail( new OrchestratorProjectionError({ threadId, cause: new Error("storage unavailable"), }), ), ), ), Effect.flip, ); expect(error.code).toBe("orchestration_error"); }), );