* fix(agents): preserve Claude thinking selections Model switches now resolve thinking against the target model's saved preference instead of resetting to the catalog default. Claude owns its configured-model defaults and legacy Fable alias handling, while persisted permission modes remain unchanged. * chore(claude): tag Fable compatibility shim * fix(app): resolve persisted model aliases * fix(models): preserve aliases for older clients * fix(models): hide compatibility aliases from listings * fix(settings): hide compatibility model aliases * test(claude): cover legacy catalog entry * test(app): expect mock thinking controls in schedules * fix(app): preserve queued thinking preferences * fix(app): reconcile schedule provider snapshots * fix(app): preserve schedule model selections * fix(claude): preserve exact configured model ids * fix(app): preserve committed thinking preferences * fix(app): preserve aliased schedule thinking
751 lines
23 KiB
TypeScript
751 lines
23 KiB
TypeScript
import type { AgentProviderDefinition } from "@getpaseo/protocol/provider-manifest";
|
|
import type {
|
|
AgentModelDefinition,
|
|
AgentProvider,
|
|
ProviderSnapshotEntry,
|
|
} from "@getpaseo/protocol/agent-types";
|
|
import {
|
|
mergeProviderPreferences,
|
|
type FormPreferences,
|
|
type ProviderPreferences,
|
|
} from "@/hooks/use-form-preferences";
|
|
import { findModelByReference } from "./model-catalog";
|
|
|
|
export interface FormInitialValues {
|
|
serverId?: string | null;
|
|
provider?: AgentProvider;
|
|
modeId?: string | null;
|
|
model?: string | null;
|
|
thinkingOptionId?: string | null;
|
|
workingDir?: string;
|
|
}
|
|
|
|
export interface FormState {
|
|
serverId: string | null;
|
|
provider: AgentProvider | null;
|
|
modeId: string;
|
|
model: string;
|
|
thinkingOptionId: string;
|
|
workingDir: string;
|
|
}
|
|
|
|
export interface UserModifiedFields {
|
|
serverId: boolean;
|
|
provider: boolean;
|
|
modeId: boolean;
|
|
model: boolean;
|
|
thinkingOptionId: boolean;
|
|
workingDir: boolean;
|
|
}
|
|
|
|
export type ProviderModelsByProvider = Map<AgentProvider, AgentModelDefinition[] | null>;
|
|
|
|
export type AgentFormResolutionState = { status: "pending" } | { status: "completed" };
|
|
|
|
export interface AgentFormReducerState {
|
|
form: FormState;
|
|
userModified: UserModifiedFields;
|
|
resolution: AgentFormResolutionState;
|
|
}
|
|
|
|
export const INITIAL_USER_MODIFIED: UserModifiedFields = {
|
|
serverId: false,
|
|
provider: false,
|
|
modeId: false,
|
|
model: false,
|
|
thinkingOptionId: false,
|
|
workingDir: false,
|
|
};
|
|
|
|
export const PENDING_AGENT_FORM_RESOLUTION: AgentFormResolutionState = { status: "pending" };
|
|
export const INITIAL_AGENT_FORM_RESOLUTION = PENDING_AGENT_FORM_RESOLUTION;
|
|
|
|
type ProviderPrefs = NonNullable<FormPreferences["providerPreferences"]>[AgentProvider];
|
|
|
|
export const RESOLVABLE_PROVIDER_STATUSES = new Set<ProviderSnapshotEntry["status"]>([
|
|
"ready",
|
|
"loading",
|
|
]);
|
|
export const SELECTABLE_PROVIDER_STATUSES = new Set<ProviderSnapshotEntry["status"]>(["ready"]);
|
|
|
|
export type AgentFormAction =
|
|
| { type: "REQUEST_RESOLUTION" }
|
|
| {
|
|
type: "COMPLETE_RESOLUTION";
|
|
initialValues: FormInitialValues | undefined;
|
|
preferences: FormPreferences | null;
|
|
providerModelsByProvider: ProviderModelsByProvider;
|
|
allowedProviderMap: Map<AgentProvider, AgentProviderDefinition>;
|
|
}
|
|
| { type: "SET_SERVER_ID"; value: string | null }
|
|
| { type: "SET_SERVER_ID_FROM_USER"; value: string | null }
|
|
| {
|
|
type: "SET_PROVIDER_FROM_USER";
|
|
provider: AgentProvider;
|
|
providerModels: AgentModelDefinition[] | null;
|
|
providerDef: AgentProviderDefinition | undefined;
|
|
providerPrefs: ProviderPrefs | undefined;
|
|
}
|
|
| {
|
|
type: "SET_PROVIDER_AND_MODEL_FROM_USER";
|
|
provider: AgentProvider;
|
|
modelId: string;
|
|
providerDef: AgentProviderDefinition | undefined;
|
|
providerModels: AgentModelDefinition[] | null;
|
|
providerPrefs?: ProviderPrefs | undefined;
|
|
}
|
|
| { type: "SET_MODE_FROM_USER"; modeId: string }
|
|
| {
|
|
type: "SET_MODEL_FROM_USER";
|
|
modelId: string;
|
|
availableModels: AgentModelDefinition[] | null;
|
|
providerPrefs: ProviderPrefs | undefined;
|
|
}
|
|
| { type: "CLEAR_PROVIDER_SELECTION_FROM_USER" }
|
|
| { type: "SET_THINKING_OPTION_FROM_USER"; thinkingOptionId: string }
|
|
| { type: "SET_WORKING_DIR"; value: string }
|
|
| { type: "SET_WORKING_DIR_FROM_USER"; value: string }
|
|
| { type: "AUTO_SELECT_SERVER"; candidateServerId: string }
|
|
| { type: "RESET" };
|
|
|
|
type CompleteResolutionAction = Extract<AgentFormAction, { type: "COMPLETE_RESOLUTION" }>;
|
|
|
|
export function normalizeSelectedModelId(modelId: string | null | undefined): string {
|
|
return typeof modelId === "string" ? modelId.trim() : "";
|
|
}
|
|
|
|
export function resolveDefaultModel(
|
|
availableModels: AgentModelDefinition[] | null,
|
|
): AgentModelDefinition | null {
|
|
if (!availableModels || availableModels.length === 0) return null;
|
|
return availableModels.find((model) => model.isDefault) ?? availableModels[0] ?? null;
|
|
}
|
|
|
|
export function resolveDefaultModelId(availableModels: AgentModelDefinition[] | null): string {
|
|
return resolveDefaultModel(availableModels)?.id ?? "";
|
|
}
|
|
|
|
function resolveCanonicalModelId(
|
|
availableModels: AgentModelDefinition[] | null,
|
|
modelId: string,
|
|
): string {
|
|
const normalizedModelId = normalizeSelectedModelId(modelId);
|
|
if (!normalizedModelId || !availableModels) return normalizedModelId;
|
|
return findModelByReference(availableModels, normalizedModelId)?.id ?? "";
|
|
}
|
|
|
|
export function resolveEffectiveModel(
|
|
availableModels: AgentModelDefinition[] | null,
|
|
modelId: string,
|
|
): AgentModelDefinition | null {
|
|
if (!availableModels || availableModels.length === 0) return null;
|
|
if (!normalizeSelectedModelId(modelId)) return null;
|
|
return findModelByReference(availableModels, modelId) ?? resolveDefaultModel(availableModels);
|
|
}
|
|
|
|
function resolvePreferredThinkingOptionId(input: {
|
|
availableModels: AgentModelDefinition[] | null;
|
|
providerPrefs: ProviderPrefs | undefined;
|
|
modelId: string;
|
|
}): string {
|
|
const model = findModelByReference(input.availableModels, input.modelId);
|
|
const modelReferences = model ? [model.id, ...(model.aliases ?? [])] : [input.modelId];
|
|
for (const modelReference of modelReferences) {
|
|
const thinkingOptionId = input.providerPrefs?.thinkingByModel?.[modelReference]?.trim();
|
|
if (thinkingOptionId) return thinkingOptionId;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
export function resolveThinkingOptionId(args: {
|
|
availableModels: AgentModelDefinition[] | null;
|
|
modelId: string;
|
|
requestedThinkingOptionId: string;
|
|
}): string {
|
|
const effectiveModel = resolveEffectiveModel(args.availableModels, args.modelId);
|
|
const thinkingOptions = effectiveModel?.thinkingOptions ?? [];
|
|
if (thinkingOptions.length === 0) return "";
|
|
|
|
const normalizedThinkingOptionId = args.requestedThinkingOptionId.trim();
|
|
if (
|
|
normalizedThinkingOptionId &&
|
|
thinkingOptions.some((option) => option.id === normalizedThinkingOptionId)
|
|
) {
|
|
return normalizedThinkingOptionId;
|
|
}
|
|
|
|
return effectiveModel?.defaultThinkingOptionId ?? thinkingOptions[0]?.id ?? "";
|
|
}
|
|
|
|
const normalizeSelectedModeId = normalizeSelectedModelId;
|
|
|
|
function resolvePreferredModeId(input: {
|
|
initialModeId?: string | null;
|
|
preferredModeId?: string | null;
|
|
providerDef: AgentProviderDefinition | undefined;
|
|
}): string {
|
|
// Saved modes are user intent. Provider create config validates unknown modes
|
|
// at submission time, so background form resolution should not erase them.
|
|
const initialModeId = normalizeSelectedModeId(input.initialModeId);
|
|
if (initialModeId) return initialModeId;
|
|
|
|
const preferredModeId = normalizeSelectedModeId(input.preferredModeId);
|
|
if (preferredModeId) return preferredModeId;
|
|
|
|
const defaultModeId = input.providerDef?.defaultModeId;
|
|
const modes = input.providerDef?.modes ?? [];
|
|
if (defaultModeId && (modes.length === 0 || modes.some((mode) => mode.id === defaultModeId))) {
|
|
return defaultModeId;
|
|
}
|
|
return modes[0]?.id ?? "";
|
|
}
|
|
|
|
export function mergeSelectedComposerPreferences(args: {
|
|
preferences: FormPreferences;
|
|
provider: AgentProvider;
|
|
updates: Partial<ProviderPreferences>;
|
|
}): FormPreferences {
|
|
return mergeProviderPreferences({
|
|
preferences: args.preferences,
|
|
provider: args.provider,
|
|
updates: args.updates,
|
|
});
|
|
}
|
|
|
|
export function combineInitialValues(
|
|
initialValues: FormInitialValues | undefined,
|
|
initialServerId: string | null,
|
|
): FormInitialValues | undefined {
|
|
const hasExplicitServerId = initialValues?.serverId !== undefined;
|
|
const serverIdFromOptions = initialServerId === null ? undefined : initialServerId;
|
|
|
|
if (!initialValues && !hasExplicitServerId && serverIdFromOptions === undefined) {
|
|
return undefined;
|
|
}
|
|
|
|
if (hasExplicitServerId) {
|
|
return { ...initialValues, serverId: initialValues?.serverId };
|
|
}
|
|
|
|
if (serverIdFromOptions !== undefined) {
|
|
return { ...initialValues, serverId: serverIdFromOptions };
|
|
}
|
|
|
|
return initialValues;
|
|
}
|
|
|
|
export function hasFormStateChanged(prev: FormState, next: FormState): boolean {
|
|
return (
|
|
prev.serverId !== next.serverId ||
|
|
prev.provider !== next.provider ||
|
|
prev.modeId !== next.modeId ||
|
|
prev.model !== next.model ||
|
|
prev.thinkingOptionId !== next.thinkingOptionId ||
|
|
prev.workingDir !== next.workingDir
|
|
);
|
|
}
|
|
|
|
export function buildProviderDefinitionMap(
|
|
providerDefinitions: AgentProviderDefinition[],
|
|
): Map<AgentProvider, AgentProviderDefinition> {
|
|
return new Map<AgentProvider, AgentProviderDefinition>(
|
|
providerDefinitions.map((definition) => [definition.id, definition]),
|
|
);
|
|
}
|
|
|
|
export function buildProviderDefinitionMapForStatuses(args: {
|
|
snapshotEntries: ProviderSnapshotEntry[] | undefined;
|
|
providerDefinitions: AgentProviderDefinition[];
|
|
statuses: ReadonlySet<ProviderSnapshotEntry["status"]>;
|
|
}): Map<AgentProvider, AgentProviderDefinition> {
|
|
if (!args.snapshotEntries?.length) {
|
|
return buildProviderDefinitionMap(args.providerDefinitions);
|
|
}
|
|
|
|
const matchingProviders = new Set(
|
|
args.snapshotEntries
|
|
.filter((entry) => args.statuses.has(entry.status) && entry.enabled)
|
|
.map((entry) => entry.provider),
|
|
);
|
|
|
|
return buildProviderDefinitionMap(
|
|
args.providerDefinitions.filter((definition) => matchingProviders.has(definition.id)),
|
|
);
|
|
}
|
|
|
|
function resolveProvider(input: {
|
|
currentProvider: AgentProvider | null;
|
|
userModified: boolean;
|
|
initialValues: FormInitialValues | undefined;
|
|
preferences: FormPreferences | null;
|
|
allowedProviderMap: Map<AgentProvider, AgentProviderDefinition>;
|
|
}): AgentProvider | null {
|
|
const { currentProvider, userModified, initialValues, preferences, allowedProviderMap } = input;
|
|
if (userModified) {
|
|
if (
|
|
currentProvider &&
|
|
allowedProviderMap.size > 0 &&
|
|
!allowedProviderMap.has(currentProvider)
|
|
) {
|
|
return null;
|
|
}
|
|
return currentProvider;
|
|
}
|
|
if (initialValues?.provider && allowedProviderMap.has(initialValues.provider)) {
|
|
return initialValues.provider;
|
|
}
|
|
if (preferences?.provider && allowedProviderMap.has(preferences.provider)) {
|
|
return preferences.provider;
|
|
}
|
|
if (currentProvider && allowedProviderMap.size > 0 && !allowedProviderMap.has(currentProvider)) {
|
|
return null;
|
|
}
|
|
return currentProvider;
|
|
}
|
|
|
|
function resolveModeId(input: {
|
|
provider: AgentProvider | null;
|
|
userModified: boolean;
|
|
currentModeId: string;
|
|
initialValues: FormInitialValues | undefined;
|
|
providerDef: AgentProviderDefinition | undefined;
|
|
providerPrefs: ProviderPrefs | undefined;
|
|
}): string {
|
|
const { provider, userModified, currentModeId, initialValues, providerDef, providerPrefs } =
|
|
input;
|
|
if (userModified) return currentModeId;
|
|
if (!provider) return "";
|
|
return resolvePreferredModeId({
|
|
initialModeId: initialValues?.modeId,
|
|
preferredModeId: providerPrefs?.mode,
|
|
providerDef,
|
|
});
|
|
}
|
|
|
|
function resolveModelField(input: {
|
|
provider: AgentProvider | null;
|
|
userModified: boolean;
|
|
currentModel: string;
|
|
initialValues: FormInitialValues | undefined;
|
|
providerPrefs: ProviderPrefs | undefined;
|
|
availableModels: AgentModelDefinition[] | null;
|
|
}): string {
|
|
const { provider, userModified, currentModel, initialValues, providerPrefs, availableModels } =
|
|
input;
|
|
if (userModified) return currentModel;
|
|
if (!provider) return "";
|
|
const initialModel = normalizeSelectedModelId(initialValues?.model);
|
|
const preferredModel = normalizeSelectedModelId(providerPrefs?.model);
|
|
const defaultModelId = resolveDefaultModelId(availableModels);
|
|
if (initialModel) {
|
|
return !availableModels
|
|
? initialModel
|
|
: resolveCanonicalModelId(availableModels, initialModel) || defaultModelId;
|
|
}
|
|
if (preferredModel) {
|
|
return !availableModels
|
|
? preferredModel
|
|
: resolveCanonicalModelId(availableModels, preferredModel) || defaultModelId;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function resolveThinkingOption(input: {
|
|
provider: AgentProvider | null;
|
|
userModified: boolean;
|
|
currentThinkingOptionId: string;
|
|
modelId: string;
|
|
initialValues: FormInitialValues | undefined;
|
|
providerPrefs: ProviderPrefs | undefined;
|
|
availableModels: AgentModelDefinition[] | null;
|
|
}): string {
|
|
const {
|
|
provider,
|
|
userModified,
|
|
currentThinkingOptionId,
|
|
modelId,
|
|
initialValues,
|
|
providerPrefs,
|
|
availableModels,
|
|
} = input;
|
|
if (!provider) return "";
|
|
if (userModified) return currentThinkingOptionId;
|
|
const initialThinkingOptionId =
|
|
typeof initialValues?.thinkingOptionId === "string"
|
|
? initialValues.thinkingOptionId.trim()
|
|
: "";
|
|
const preferredThinking = resolvePreferredThinkingOptionId({
|
|
availableModels,
|
|
providerPrefs,
|
|
modelId,
|
|
});
|
|
if (initialThinkingOptionId.length > 0) return initialThinkingOptionId;
|
|
if (preferredThinking.length > 0) return preferredThinking;
|
|
return "";
|
|
}
|
|
|
|
export function resolveFormState(
|
|
initialValues: FormInitialValues | undefined,
|
|
preferences: FormPreferences | null,
|
|
availableModels: AgentModelDefinition[] | null,
|
|
userModified: UserModifiedFields,
|
|
currentState: FormState,
|
|
allowedProviderMap: Map<AgentProvider, AgentProviderDefinition>,
|
|
): FormState {
|
|
const result = { ...currentState };
|
|
|
|
result.provider = resolveProvider({
|
|
currentProvider: result.provider,
|
|
userModified: userModified.provider,
|
|
initialValues,
|
|
preferences,
|
|
allowedProviderMap,
|
|
});
|
|
|
|
const providerDef = result.provider ? allowedProviderMap.get(result.provider) : undefined;
|
|
const providerPrefs = result.provider
|
|
? preferences?.providerPreferences?.[result.provider]
|
|
: undefined;
|
|
|
|
result.modeId = resolveModeId({
|
|
provider: result.provider,
|
|
userModified: userModified.modeId,
|
|
currentModeId: result.modeId,
|
|
initialValues,
|
|
providerDef,
|
|
providerPrefs,
|
|
});
|
|
|
|
result.model = resolveModelField({
|
|
provider: result.provider,
|
|
userModified: userModified.model,
|
|
currentModel: result.model,
|
|
initialValues,
|
|
providerPrefs,
|
|
availableModels,
|
|
});
|
|
|
|
result.thinkingOptionId = resolveThinkingOption({
|
|
provider: result.provider,
|
|
userModified: userModified.thinkingOptionId,
|
|
currentThinkingOptionId: result.thinkingOptionId,
|
|
modelId: result.model,
|
|
initialValues,
|
|
providerPrefs,
|
|
availableModels,
|
|
});
|
|
|
|
if (result.provider && availableModels) {
|
|
result.thinkingOptionId = resolveThinkingOptionId({
|
|
availableModels,
|
|
modelId: result.model,
|
|
requestedThinkingOptionId: result.thinkingOptionId,
|
|
});
|
|
}
|
|
|
|
if (!userModified.serverId && initialValues?.serverId !== undefined) {
|
|
result.serverId = initialValues.serverId;
|
|
}
|
|
|
|
if (!userModified.workingDir && initialValues?.workingDir !== undefined) {
|
|
result.workingDir = initialValues.workingDir;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
export function resolveFormStateFromProviderModels(
|
|
initialValues: FormInitialValues | undefined,
|
|
preferences: FormPreferences | null,
|
|
providerModelsByProvider: ProviderModelsByProvider,
|
|
userModified: UserModifiedFields,
|
|
currentState: FormState,
|
|
allowedProviderMap: Map<AgentProvider, AgentProviderDefinition>,
|
|
): FormState {
|
|
const providerResolved = resolveFormState(
|
|
initialValues,
|
|
preferences,
|
|
null,
|
|
userModified,
|
|
currentState,
|
|
allowedProviderMap,
|
|
);
|
|
const availableModels = providerResolved.provider
|
|
? (providerModelsByProvider.get(providerResolved.provider) ?? null)
|
|
: null;
|
|
|
|
return resolveFormState(
|
|
initialValues,
|
|
preferences,
|
|
availableModels,
|
|
userModified,
|
|
currentState,
|
|
allowedProviderMap,
|
|
);
|
|
}
|
|
|
|
function pickNextModelForProvider(input: {
|
|
providerModels: AgentModelDefinition[] | null;
|
|
providerPrefs: ProviderPrefs | undefined;
|
|
}): string {
|
|
const { providerModels, providerPrefs } = input;
|
|
const preferredModel = normalizeSelectedModelId(providerPrefs?.model);
|
|
const defaultModelId = resolveDefaultModelId(providerModels);
|
|
if (preferredModel) {
|
|
if (!providerModels) return preferredModel;
|
|
return resolveCanonicalModelId(providerModels, preferredModel) || defaultModelId;
|
|
}
|
|
return defaultModelId;
|
|
}
|
|
|
|
function pickNextModeForProvider(input: {
|
|
providerDef: AgentProviderDefinition | undefined;
|
|
providerPrefs: ProviderPrefs | undefined;
|
|
}): string {
|
|
const { providerDef, providerPrefs } = input;
|
|
return resolvePreferredModeId({
|
|
preferredModeId: providerPrefs?.mode,
|
|
providerDef,
|
|
});
|
|
}
|
|
|
|
function pickNextModeForProviderAndModel(input: {
|
|
currentProvider: AgentProvider | null;
|
|
currentModeId: string;
|
|
provider: AgentProvider;
|
|
providerDef: AgentProviderDefinition | undefined;
|
|
providerPrefs: ProviderPrefs | undefined;
|
|
}): string {
|
|
const currentModeId = normalizeSelectedModeId(input.currentModeId);
|
|
if (input.currentProvider === input.provider && currentModeId) return currentModeId;
|
|
return pickNextModeForProvider({
|
|
providerDef: input.providerDef,
|
|
providerPrefs: input.providerPrefs,
|
|
});
|
|
}
|
|
|
|
function pickNextThinkingOptionForProvider(input: {
|
|
providerModels: AgentModelDefinition[] | null;
|
|
providerPrefs: ProviderPrefs | undefined;
|
|
modelId: string;
|
|
}): string {
|
|
const { providerModels, providerPrefs, modelId } = input;
|
|
const preferredThinking = resolvePreferredThinkingOptionId({
|
|
availableModels: providerModels,
|
|
providerPrefs,
|
|
modelId,
|
|
});
|
|
return resolveThinkingOptionId({
|
|
availableModels: providerModels,
|
|
modelId,
|
|
requestedThinkingOptionId: preferredThinking,
|
|
});
|
|
}
|
|
|
|
function pickNextThinkingOptionForTarget(input: {
|
|
availableModels: AgentModelDefinition[] | null;
|
|
providerPrefs: ProviderPrefs | undefined;
|
|
modelId: string;
|
|
currentModelId: string;
|
|
currentThinkingOptionId: string;
|
|
isSameProvider: boolean;
|
|
}): string {
|
|
const requestedThinkingOptionId =
|
|
input.isSameProvider &&
|
|
resolveCanonicalModelId(input.availableModels, input.currentModelId) === input.modelId
|
|
? input.currentThinkingOptionId
|
|
: resolvePreferredThinkingOptionId({
|
|
availableModels: input.availableModels,
|
|
providerPrefs: input.providerPrefs,
|
|
modelId: input.modelId,
|
|
});
|
|
return resolveThinkingOptionId({
|
|
availableModels: input.availableModels,
|
|
modelId: input.modelId,
|
|
requestedThinkingOptionId,
|
|
});
|
|
}
|
|
|
|
function completeResolution(
|
|
state: AgentFormReducerState,
|
|
action: CompleteResolutionAction,
|
|
): AgentFormReducerState {
|
|
if (state.resolution.status === "completed") {
|
|
return state;
|
|
}
|
|
const resolved = resolveFormStateFromProviderModels(
|
|
action.initialValues,
|
|
action.preferences,
|
|
action.providerModelsByProvider,
|
|
state.userModified,
|
|
state.form,
|
|
action.allowedProviderMap,
|
|
);
|
|
const nextState = { ...state, resolution: { status: "completed" } as const };
|
|
if (!hasFormStateChanged(state.form, resolved)) return nextState;
|
|
return { ...nextState, form: resolved };
|
|
}
|
|
|
|
export function resolveAgentForm(
|
|
state: AgentFormReducerState,
|
|
action: AgentFormAction,
|
|
): AgentFormReducerState {
|
|
switch (action.type) {
|
|
case "REQUEST_RESOLUTION":
|
|
return {
|
|
...state,
|
|
userModified: INITIAL_USER_MODIFIED,
|
|
resolution: PENDING_AGENT_FORM_RESOLUTION,
|
|
};
|
|
|
|
case "COMPLETE_RESOLUTION":
|
|
return completeResolution(state, action);
|
|
|
|
case "SET_SERVER_ID":
|
|
return { ...state, form: { ...state.form, serverId: action.value } };
|
|
|
|
case "SET_SERVER_ID_FROM_USER":
|
|
return {
|
|
...state,
|
|
form: { ...state.form, serverId: action.value },
|
|
userModified: { ...state.userModified, serverId: true },
|
|
};
|
|
|
|
case "SET_PROVIDER_FROM_USER": {
|
|
const nextModelId = pickNextModelForProvider({
|
|
providerModels: action.providerModels,
|
|
providerPrefs: action.providerPrefs,
|
|
});
|
|
const nextModeId = pickNextModeForProvider({
|
|
providerDef: action.providerDef,
|
|
providerPrefs: action.providerPrefs,
|
|
});
|
|
const nextThinkingOptionId = pickNextThinkingOptionForProvider({
|
|
providerModels: action.providerModels,
|
|
providerPrefs: action.providerPrefs,
|
|
modelId: nextModelId,
|
|
});
|
|
return {
|
|
...state,
|
|
form: {
|
|
...state.form,
|
|
provider: action.provider,
|
|
modeId: nextModeId,
|
|
model: nextModelId,
|
|
thinkingOptionId: nextThinkingOptionId,
|
|
},
|
|
userModified: { ...state.userModified, provider: true },
|
|
};
|
|
}
|
|
|
|
case "SET_PROVIDER_AND_MODEL_FROM_USER": {
|
|
const normalizedModelId = resolveCanonicalModelId(action.providerModels, action.modelId);
|
|
const nextModelId = normalizedModelId || resolveDefaultModelId(action.providerModels);
|
|
const nextThinkingOptionId = pickNextThinkingOptionForTarget({
|
|
availableModels: action.providerModels,
|
|
modelId: nextModelId,
|
|
providerPrefs: action.providerPrefs,
|
|
currentModelId: state.form.model,
|
|
currentThinkingOptionId: state.form.thinkingOptionId,
|
|
isSameProvider: state.form.provider === action.provider,
|
|
});
|
|
const nextModeId = pickNextModeForProviderAndModel({
|
|
currentProvider: state.form.provider,
|
|
currentModeId: state.form.modeId,
|
|
provider: action.provider,
|
|
providerDef: action.providerDef,
|
|
providerPrefs: action.providerPrefs,
|
|
});
|
|
return {
|
|
...state,
|
|
form: {
|
|
...state.form,
|
|
provider: action.provider,
|
|
model: nextModelId,
|
|
modeId: nextModeId,
|
|
thinkingOptionId: nextThinkingOptionId,
|
|
},
|
|
userModified: { ...state.userModified, provider: true, model: true },
|
|
};
|
|
}
|
|
|
|
case "SET_MODE_FROM_USER":
|
|
return {
|
|
...state,
|
|
form: { ...state.form, modeId: action.modeId },
|
|
userModified: { ...state.userModified, modeId: true },
|
|
};
|
|
|
|
case "SET_MODEL_FROM_USER": {
|
|
const normalizedModelId = resolveCanonicalModelId(action.availableModels, action.modelId);
|
|
const nextModelId = normalizedModelId || resolveDefaultModelId(action.availableModels);
|
|
const nextThinkingOptionId = pickNextThinkingOptionForTarget({
|
|
availableModels: action.availableModels,
|
|
modelId: nextModelId,
|
|
providerPrefs: action.providerPrefs,
|
|
currentModelId: state.form.model,
|
|
currentThinkingOptionId: state.form.thinkingOptionId,
|
|
isSameProvider: true,
|
|
});
|
|
return {
|
|
...state,
|
|
form: {
|
|
...state.form,
|
|
model: nextModelId,
|
|
thinkingOptionId: nextThinkingOptionId,
|
|
},
|
|
userModified: { ...state.userModified, model: true },
|
|
};
|
|
}
|
|
|
|
case "CLEAR_PROVIDER_SELECTION_FROM_USER":
|
|
return {
|
|
...state,
|
|
form: {
|
|
...state.form,
|
|
provider: null,
|
|
model: "",
|
|
modeId: "",
|
|
thinkingOptionId: "",
|
|
},
|
|
userModified: {
|
|
...state.userModified,
|
|
provider: true,
|
|
model: true,
|
|
modeId: true,
|
|
thinkingOptionId: true,
|
|
},
|
|
};
|
|
|
|
case "SET_THINKING_OPTION_FROM_USER":
|
|
return {
|
|
...state,
|
|
form: { ...state.form, thinkingOptionId: action.thinkingOptionId },
|
|
userModified: { ...state.userModified, thinkingOptionId: true },
|
|
};
|
|
|
|
case "SET_WORKING_DIR":
|
|
return { ...state, form: { ...state.form, workingDir: action.value } };
|
|
|
|
case "SET_WORKING_DIR_FROM_USER":
|
|
return {
|
|
...state,
|
|
form: { ...state.form, workingDir: action.value },
|
|
userModified: { ...state.userModified, workingDir: true },
|
|
};
|
|
|
|
case "AUTO_SELECT_SERVER":
|
|
if (state.form.serverId) return state;
|
|
return { ...state, form: { ...state.form, serverId: action.candidateServerId } };
|
|
|
|
case "RESET":
|
|
return {
|
|
...state,
|
|
userModified: INITIAL_USER_MODIFIED,
|
|
resolution: INITIAL_AGENT_FORM_RESOLUTION,
|
|
};
|
|
default:
|
|
throw new Error("unreachable");
|
|
}
|
|
}
|