1import { assert, it } from "@effect/vitest";
2import {
3 type ModelSelection,
4 type OrchestrationV2AppThread,
5 ProjectId,
6 ProviderInstanceId,
7 type RuntimeMode,
8 type ServerProvider,
9 ThreadId,
10} from "@t3tools/contracts";
11import * as DateTime from "effect/DateTime";
12import * as Effect from "effect/Effect";
13import * as Layer from "effect/Layer";
14import * as Option from "effect/Option";
15import * as Stream from "effect/Stream";
16
17import type { ProviderInstance } from "@t3tools/provider-core/server/driver";
18import * as ProviderInstanceRegistry from "../provider/ProviderInstanceRegistry.ts";
19import * as ProjectStore from "./ProjectStore.ts";
20import * as RuntimePolicy from "./RuntimePolicy.ts";
21
22const projectId = ProjectId.make("project:runtime-policy");
23const providerInstanceId = ProviderInstanceId.make("codex");
24const modelSelection = {
25 instanceId: providerInstanceId,
26 model: "gpt-5.5",
27} satisfies ModelSelection;
28
29function makeThread(input: {
30 readonly now: DateTime.Utc;
31 readonly worktreePath: string | null;
32 readonly runtimeMode?: RuntimeMode;
33}): OrchestrationV2AppThread {
34 const threadId = ThreadId.make("thread:runtime-policy");
35 return {
36 createdBy: "user",
37 creationSource: "web",
38 id: threadId,
39 projectId,
40 title: "Runtime policy",
41 providerInstanceId,
42 modelSelection,
43 runtimeMode: input.runtimeMode ?? "full-access",
44 interactionMode: "default",
45 branch: null,
46 worktreePath: input.worktreePath,
47 activeProviderThreadId: null,
48 lineage: {
49 parentThreadId: null,
50 relationshipToParent: null,
51 rootThreadId: threadId,
52 },
53 forkedFrom: null,
54 createdAt: input.now,
55 updatedAt: input.now,
56 archivedAt: null,
57 settledOverride: null,
58 settledAt: null,
59 lastVisitedAt: null,
60 deletedAt: null,
61 };
62}
63
64// Grok's instance offers no Auto-accept edits; the Codex instance advertises no
65// restriction.
66const grokInstanceId = ProviderInstanceId.make("grok");
67const supportedRuntimeModesByInstance = new Map<ProviderInstanceId, ReadonlyArray<RuntimeMode>>([
68 [grokInstanceId, ["approval-required", "auto", "full-access"]],
69]);
70const providerInstanceFor = (instanceId: ProviderInstanceId) =>
71 ({
72 snapshot: {
73 getSnapshot: Effect.succeed({
74 supportedRuntimeModes: supportedRuntimeModesByInstance.get(instanceId),
75 } as ServerProvider),
76 },
77 }) as ProviderInstance;
78
79const layerTest = RuntimePolicy.layerFromProjectStore.pipe(
80 Layer.provide(
81 Layer.succeed(ProviderInstanceRegistry.ProviderInstanceRegistry, {
82 getInstance: (instanceId) => Effect.succeed(providerInstanceFor(instanceId)),
83 listInstances: Effect.succeed([]),
84 listUnavailable: Effect.succeed([]),
85 streamChanges: Stream.empty,
86 subscribeChanges: Effect.never,
87 }),
88 ),
89 Layer.provide(
90 Layer.mock(ProjectStore.ProjectStoreV2)({
91 get: () =>
92 Effect.succeed(
93 Option.some({
94 projectId,
95 title: "Project",
96 workspaceRoot: "/project-root",
97 defaultModelSelection: null,
98 defaultThreadEnvMode: null,
99 autoPull: false,
100 faviconPath: null,
101 projectIcon: null,
102 scripts: [],
103 createdAt: "2026-06-21T00:00:00.000Z",
104 updatedAt: "2026-06-21T00:00:00.000Z",
105 deletedAt: null,
106 }),
107 ),
108 }),
109 ),
110);
111
112it.layer(layerTest)("RuntimePolicyV2", (it) => {
113 it.effect("uses the project root for local-checkout threads", () =>
114 Effect.gen(function* () {
115 const policy = yield* RuntimePolicy.RuntimePolicyV2;
116 const now = yield* DateTime.now;
117 const resolved = yield* policy.resolve({
118 thread: makeThread({ now, worktreePath: null }),
119 modelSelection,
120 });
121 assert.equal(resolved.cwd, "/project-root");
122 }),
123 );
124
125 it.effect("prefers a provisioned worktree over the project root", () =>
126 Effect.gen(function* () {
127 const policy = yield* RuntimePolicy.RuntimePolicyV2;
128 const now = yield* DateTime.now;
129 const resolved = yield* policy.resolve({
130 thread: makeThread({ now, worktreePath: "/project-worktree" }),
131 modelSelection,
132 });
133 assert.equal(resolved.cwd, "/project-worktree");
134 }),
135 );
136
137 it.effect("runs a mode the provider does not offer in Supervised", () =>
138 Effect.gen(function* () {
139 const policy = yield* RuntimePolicy.RuntimePolicyV2;
140 const now = yield* DateTime.now;
141 const modeFor = (instanceId: ProviderInstanceId, runtimeMode: RuntimeMode) =>
142 policy
143 .resolve({
144 thread: makeThread({ now, worktreePath: null, runtimeMode }),
145 modelSelection: { instanceId, model: "test-model" },
146 })
147 .pipe(Effect.map((resolved) => resolved.runtimeMode));
148
149 assert.equal(yield* modeFor(grokInstanceId, "auto-accept-edits"), "approval-required");
150 assert.equal(yield* modeFor(grokInstanceId, "auto"), "auto");
151 assert.equal(yield* modeFor(grokInstanceId, "full-access"), "full-access");
152 // A provider that advertises no restriction runs every mode as stored.
153 assert.equal(yield* modeFor(providerInstanceId, "auto-accept-edits"), "auto-accept-edits");
154 }),
155 );
156});