1import { expect, it } from "@effect/vitest";
2import { EnvironmentId, ThreadId } from "@t3tools/contracts";
3import * as Effect from "effect/Effect";
4import * as Layer from "effect/Layer";
5
6import * as ThreadManagement from "../orchestration-v2/ThreadManagementService.ts";
7import * as McpInvocationContext from "./McpInvocationContext.ts";
8import { liveThreadShell } from "./McpToolAccess.testkit.ts";
9import { readThread } from "./threadAccess.ts";
10
11const bareId = ThreadId.make("5d0b4a3e-8c1f-4a52-9d37-2f6c7e1b9a04");
12const client: McpInvocationContext.McpInvocationScope = {
13 environmentId: EnvironmentId.make("environment:thread-access"),
14 requestNamespace: "client:thread-access",
15 thread: undefined,
16 client: { sessionId: "thread-access", label: "Claude Code", access: "full-access" },
17 capabilities: new Set(["orchestration"]),
18 issuedAt: 1,
19};
20
21const readBare = (existing: ReadonlyArray<ThreadId>) =>
22 readThread(bareId).pipe(
23 Effect.flip,
24 Effect.provideService(McpInvocationContext.McpInvocationContext, client),
25 Effect.provide(
26 Layer.mock(ThreadManagement.ThreadManagementService)({
27 getThreadShell: (threadId) =>
28 Effect.succeed(existing.includes(threadId) ? liveThreadShell(threadId) : null),
29 }),
30 ),
31 );
32
33it.effect("points a bare uuid at the mcp: thread it belongs to", () =>
34 Effect.gen(function* () {
35 const error = yield* readBare([ThreadId.make(`mcp:${bareId}`)]);
36
37 expect(error.code).toBe("thread_not_found");
38 expect(error.message).toBe(`The thread was not found. Did you mean \`mcp:${bareId}\`?`);
39 }),
40);