1import { describe, expect, it } from "@effect/vitest";
2
3import { ACP_SESSION_MODE_OPTION_ID, acpProviderOptionDescriptors } from "./sessionConfig.ts";
4
5describe("acpProviderOptionDescriptors", () => {
6 it("maps non-model select options and excludes model and collaboration categories", () => {
7 const descriptors = acpProviderOptionDescriptors({
8 configOptions: [
9 {
10 id: "model",
11 name: "Model",
12 category: "model",
13 type: "select",
14 currentValue: "gpt-5.6-sol",
15 options: [{ value: "gpt-5.6-sol", name: "GPT-5.6-Sol" }],
16 },
17 {
18 id: "collaboration_mode",
19 name: "Collaboration mode",
20 category: "collaboration_mode",
21 type: "select",
22 currentValue: "default",
23 options: [
24 { value: "default", name: "Default" },
25 { value: "plan", name: "Plan" },
26 ],
27 },
28 {
29 id: "reasoning_effort",
30 name: "Reasoning effort",
31 category: "thought_level",
32 type: "select",
33 currentValue: "medium",
34 options: [
35 { value: "low", name: "Low" },
36 { value: "medium", name: "Medium", description: "Balanced" },
37 ],
38 },
39 ],
40 modeState: undefined,
41 });
42
43 expect(descriptors).toEqual([
44 {
45 id: "reasoning_effort",
46 label: "Reasoning effort",
47 type: "select",
48 options: [
49 { id: "low", label: "Low" },
50 { id: "medium", label: "Medium", description: "Balanced" },
51 ],
52 currentValue: "medium",
53 },
54 ]);
55 });
56
57 it("maps boolean session options", () => {
58 const descriptors = acpProviderOptionDescriptors({
59 configOptions: [
60 {
61 id: "fast_mode",
62 name: "Fast mode",
63 description: "Use lower-latency inference.",
64 type: "boolean",
65 currentValue: true,
66 },
67 ],
68 modeState: undefined,
69 });
70
71 expect(descriptors).toEqual([
72 {
73 id: "fast_mode",
74 label: "Fast mode",
75 description: "Use lower-latency inference.",
76 type: "boolean",
77 currentValue: true,
78 },
79 ]);
80 });
81
82 it("flattens grouped select choices and drops empty or duplicate values", () => {
83 const descriptors = acpProviderOptionDescriptors({
84 configOptions: [
85 {
86 id: "sandbox",
87 name: "Sandbox",
88 type: "select",
89 currentValue: "workspace",
90 options: [
91 {
92 groupId: "standard",
93 name: "Standard",
94 options: [
95 { value: "workspace", name: "Workspace" },
96 { value: " ", name: "Blank" },
97 ],
98 },
99 {
100 groupId: "extra",
101 name: "Extra",
102 options: [
103 { value: "workspace", name: "Duplicate" },
104 { value: "full", name: "Full" },
105 ],
106 },
107 ],
108 },
109 ],
110 modeState: undefined,
111 });
112
113 expect(descriptors).toEqual([
114 {
115 id: "sandbox",
116 label: "Sandbox",
117 type: "select",
118 options: [
119 { id: "workspace", label: "Workspace" },
120 { id: "full", label: "Full" },
121 ],
122 currentValue: "workspace",
123 },
124 ]);
125 });
126
127 it("synthesizes a mode descriptor for agents without a mode config option", () => {
128 const descriptors = acpProviderOptionDescriptors({
129 configOptions: [],
130 modeState: {
131 currentModeId: "safe",
132 availableModes: [
133 { id: "safe", name: "Safe" },
134 { id: "yolo", name: "YOLO", description: "No approvals" },
135 ],
136 },
137 });
138
139 expect(descriptors).toEqual([
140 {
141 id: ACP_SESSION_MODE_OPTION_ID,
142 label: "Mode",
143 description: "Session mode advertised by the ACP agent.",
144 type: "select",
145 options: [
146 { id: "safe", label: "Safe" },
147 { id: "yolo", label: "YOLO", description: "No approvals" },
148 ],
149 currentValue: "safe",
150 },
151 ]);
152 });
153
154 it("suppresses the synthetic mode descriptor when modes duplicate a config option", () => {
155 const descriptors = acpProviderOptionDescriptors({
156 configOptions: [
157 {
158 id: "thinking",
159 name: "Thinking",
160 category: "thought_level",
161 type: "select",
162 currentValue: "medium",
163 options: [
164 { value: "off", name: "Thinking: off" },
165 { value: "medium", name: "Thinking: medium" },
166 ],
167 },
168 ],
169 modeState: {
170 currentModeId: "medium",
171 availableModes: [
172 { id: "off", name: "Thinking: off" },
173 { id: "medium", name: "Thinking: medium" },
174 ],
175 },
176 });
177
178 expect(descriptors.map((descriptor) => descriptor.id)).toEqual(["thinking"]);
179 });
180
181 it("does not let an unrelated select option hide the session modes API", () => {
182 const descriptors = acpProviderOptionDescriptors({
183 configOptions: [
184 {
185 id: "unrelated",
186 name: "Unrelated",
187 type: "select",
188 currentValue: "safe",
189 options: [
190 { value: "safe", name: "Safe" },
191 { value: "yolo", name: "YOLO" },
192 ],
193 },
194 ],
195 modeState: {
196 currentModeId: "safe",
197 availableModes: [
198 { id: "safe", name: "Safe" },
199 { id: "yolo", name: "YOLO" },
200 ],
201 },
202 });
203
204 expect(descriptors.map((descriptor) => descriptor.id)).toEqual([
205 "unrelated",
206 ACP_SESSION_MODE_OPTION_ID,
207 ]);
208 });
209
210 it("preserves canonical opaque option values and omits values the wire would mutate", () => {
211 const exactValue = "value:with:opaque-markers";
212 const descriptors = acpProviderOptionDescriptors({
213 configOptions: [
214 {
215 id: "opaque",
216 name: "Opaque",
217 type: "select",
218 currentValue: exactValue,
219 options: [
220 { value: exactValue, name: "Exact" },
221 { value: " value with spaces ", name: "Would be trimmed" },
222 { value: "x".repeat(257), name: "Too long" },
223 ],
224 },
225 ],
226 modeState: undefined,
227 });
228
229 expect(descriptors[0]).toMatchObject({
230 id: "opaque",
231 currentValue: exactValue,
232 options: [{ id: exactValue, label: "Exact" }],
233 });
234 });
235
236 it("suppresses the synthetic mode descriptor when a mode-category option exists", () => {
237 const descriptors = acpProviderOptionDescriptors({
238 configOptions: [
239 {
240 id: "mode",
241 name: "Mode",
242 category: "mode",
243 type: "select",
244 currentValue: "agent",
245 options: [
246 { value: "read-only", name: "Read-only" },
247 { value: "agent", name: "Agent" },
248 ],
249 },
250 ],
251 modeState: {
252 currentModeId: "agent",
253 availableModes: [
254 { id: "read-only", name: "Read-only" },
255 { id: "agent", name: "Agent" },
256 { id: "agent-full-access", name: "Agent (full access)" },
257 ],
258 },
259 });
260
261 expect(descriptors.map((descriptor) => descriptor.id)).toEqual(["mode"]);
262 });
263});