1import {
2 getPromptControlledTraits,
3 getTraitSelectChange,
4} from "@t3tools/client-runtime/model-traits";
5import {
6 ProviderInstanceId,
7 type ManagedPolicySummary,
8 type ProviderOptionDescriptor,
9 type RuntimeMode,
10} from "@t3tools/contracts";
11import { describe, expect, it } from "vite-plus/test";
12
13import {
14 applyProviderOptionSelection,
15 resolveProviderOptionDescriptors,
16 withImplicitFastModeSelection,
17} from "../../lib/providerOptions";
18import {
19 RUNTIME_MODE_CHOICES,
20 resolveNewThreadRuntimeMode,
21 selectableChoices,
22 withManagedRuntimeModeLimits,
23} from "./thread-settings-options";
24
25const policy = (organization: string, maxRuntimeMode?: RuntimeMode): ManagedPolicySummary => ({
26 organization,
27 lockedSettings: [],
28 ...(maxRuntimeMode === undefined ? {} : { limits: { maxRuntimeMode } }),
29});
30
31describe("managed runtime mode limits", () => {
32 it("disables modes above the strictest target's ceiling with that target's reason", () => {
33 const choices = withManagedRuntimeModeLimits(RUNTIME_MODE_CHOICES, [
34 policy("Wide Corp", "auto"),
35 undefined,
36 policy("Narrow Corp", "auto-accept-edits"),
37 ]);
38
39 expect(choices.map((choice) => [choice.mode, choice.disabledReason])).toEqual([
40 ["approval-required", undefined],
41 ["auto-accept-edits", undefined],
42 ["auto", "Narrow Corp limits threads to Auto-accept edits."],
43 ["full-access", "Wide Corp limits threads to Auto."],
44 ]);
45 });
46
47 it("drops a new thread's draft mode above the ceiling for the default", () => {
48 const capped = policy("Corp", "auto");
49 expect(resolveNewThreadRuntimeMode("full-access", "auto", capped)).toBe("auto");
50 expect(resolveNewThreadRuntimeMode("approval-required", "auto", capped)).toBe(
51 "approval-required",
52 );
53 expect(resolveNewThreadRuntimeMode(undefined, "auto", capped)).toBe("auto");
54 expect(resolveNewThreadRuntimeMode("full-access", "auto", undefined)).toBe("full-access");
55 });
56});
57
58const effortDescriptor: Extract<ProviderOptionDescriptor, { type: "select" }> = {
59 id: "effort",
60 label: "Reasoning",
61 type: "select",
62 options: [
63 { id: "low", label: "Low" },
64 { id: "medium", label: "Medium", isDefault: true },
65 { id: "high", label: "High" },
66 { id: "ultrathink", label: "Ultrathink" },
67 { id: "ultracode", label: "Ultracode" },
68 ],
69 currentValue: "high",
70 promptInjectedValues: ["ultrathink"],
71};
72
73describe("fast mode switch", () => {
74 // regression: #29729 Cursor reports Fast on by default, but its picker shows an unset fastMode as off
75 it("shows an unset fast mode as off, saves it as off and turns it on when toggled", () => {
76 const capabilities = {
77 optionDescriptors: [
78 { id: "fastMode", label: "Fast", type: "boolean", currentValue: true } as const,
79 ],
80 };
81 const selection = { instanceId: ProviderInstanceId.make("cursor"), model: "composer-2.5" };
82 const descriptors = resolveProviderOptionDescriptors({ capabilities, selections: undefined });
83 const shown = descriptors.find((descriptor) => descriptor.id === "fastMode")?.currentValue;
84
85 expect({
86 shown,
87 saved: withImplicitFastModeSelection(selection, [
88 { instanceId: selection.instanceId, models: [{ slug: selection.model, capabilities }] },
89 ]).options,
90 afterToggle: applyProviderOptionSelection(descriptors, { id: "fastMode", value: !shown }),
91 }).toEqual({
92 shown: false,
93 saved: [{ id: "fastMode", value: false }],
94 afterToggle: [{ id: "fastMode", value: true }],
95 });
96 });
97});
98
99describe("selectableChoices", () => {
100 // regression: #21406 bring model controls in line with web
101 it("leaves out prompt-injected choices and keeps workflow choices in declared order", () => {
102 expect(selectableChoices(effortDescriptor).map((choice) => choice.id)).toEqual([
103 "low",
104 "medium",
105 "high",
106 "ultracode",
107 ]);
108 });
109});
110
111describe("prompt-controlled reasoning", () => {
112 it("removes the prefix when choosing a regular effort and saves Ultracode as an option", () => {
113 const descriptors = [effortDescriptor];
114 const prompt = "Ultrathink:\nsolve it";
115 expect(
116 getTraitSelectChange(
117 { ...getPromptControlledTraits(descriptors, prompt), descriptors },
118 effortDescriptor,
119 prompt,
120 "ultracode",
121 ),
122 ).toEqual({ prompt: "solve it", modelOptions: [{ id: "effort", value: "ultracode" }] });
123 });
124
125 it("keeps body-controlled Ultrathink locked", () => {
126 const descriptors = [effortDescriptor];
127 const prompt = "please ultrathink about this";
128 const selected = { ...getPromptControlledTraits(descriptors, prompt), descriptors };
129 expect(selected.ultrathinkInBodyText).toBe(true);
130 expect(getTraitSelectChange(selected, effortDescriptor, prompt, "low")).toBeNull();
131 });
132});