1import { assert, it } from "@effect/vitest";
2import {
3 CommandId,
4 ProviderInstanceId,
5 ThreadId,
6 type OrchestrationV2ThreadProjection,
7} from "@t3tools/contracts";
8import * as Effect from "effect/Effect";
9import * as Layer from "effect/Layer";
10
11import * as ThreadLifecycle from "./ThreadLifecycleService.ts";
12import * as ThreadManagement from "./ThreadManagementService.ts";
13
14it.effect("maps application lifecycle operations to V2-native commands", () => {
15 const threadId = ThreadId.make("thread_lifecycle_service");
16 const commands: Array<string> = [];
17 const projection = { thread: { id: threadId } } as OrchestrationV2ThreadProjection;
18 const layer = ThreadLifecycle.layer.pipe(
19 Layer.provide(
20 Layer.mock(ThreadManagement.ThreadManagementService)({
21 dispatch: (command) => {
22 commands.push(command.type);
23 return Effect.succeed({ sequence: commands.length, storedEvents: [] });
24 },
25 getThreadRecords: () => Effect.succeed(projection),
26 }),
27 ),
28 );
29 return Effect.gen(function* () {
30 const service = yield* ThreadLifecycle.ThreadLifecycleService;
31 yield* service.archive({ commandId: CommandId.make("archive"), threadId });
32 yield* service.unarchive({ commandId: CommandId.make("unarchive"), threadId });
33 yield* service.updateMetadata({ commandId: CommandId.make("metadata"), threadId, title: "T" });
34 yield* service.setRuntimeMode({
35 commandId: CommandId.make("runtime"),
36 threadId,
37 runtimeMode: "approval-required",
38 });
39 yield* service.setInteractionMode({
40 commandId: CommandId.make("interaction"),
41 threadId,
42 interactionMode: "plan",
43 });
44 yield* service.setModelSelection({
45 commandId: CommandId.make("model"),
46 threadId,
47 modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.2" },
48 });
49 yield* service.delete({ commandId: CommandId.make("delete"), threadId });
50 assert.deepEqual(commands, [
51 "thread.archive",
52 "thread.unarchive",
53 "thread.metadata.update",
54 "thread.runtime-mode.set",
55 "thread.interaction-mode.set",
56 "thread.model-selection.set",
57 "thread.delete",
58 ]);
59 }).pipe(Effect.provide(layer));
60});