| 28 | * Subscribes to persisted preferences so model/thinking/agent changes propagate automatically. |
| 29 | */ |
| 30 | export function useSendMessageOptions(workspaceId: string): SendMessageOptionsWithBase { |
| 31 | const [thinkingLevel] = useThinkingLevel(); |
| 32 | const { agentId, disableWorkspaceAgents } = useAgent(); |
| 33 | const { workspaceMetadata } = useWorkspaceContext(); |
| 34 | const { options: providerOptions } = useProviderOptions(); |
| 35 | |
| 36 | // Subscribe to the global default model preference so backend-seeded values apply |
| 37 | // immediately on fresh origins (e.g., when switching ports). |
| 38 | const [defaultModelPref] = usePersistedState<string>( |
| 39 | DEFAULT_MODEL_KEY, |
| 40 | WORKSPACE_DEFAULTS.model, |
| 41 | { listener: true } |
| 42 | ); |
| 43 | const defaultModel = normalizeModelPreference(defaultModelPref, WORKSPACE_DEFAULTS.model); |
| 44 | |
| 45 | // Workspace-scoped model preference. If unset, fall back to metadata, then global default. |
| 46 | // Note: we intentionally *don't* pass defaultModel as the usePersistedState initialValue; |
| 47 | // initialValue is sticky and would lock in the fallback before startup seeding. |
| 48 | const [preferredModel] = usePersistedState<string | null>(getModelKey(workspaceId), null, { |
| 49 | listener: true, |
| 50 | }); |
| 51 | |
| 52 | // Subscribe to local override state so toggles apply immediately. |
| 53 | // If undefined, the backend will apply the PostHog assignment. |
| 54 | const programmaticToolCalling = useExperimentOverrideValue( |
| 55 | EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING |
| 56 | ); |
| 57 | const programmaticToolCallingExclusive = useExperimentOverrideValue( |
| 58 | EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING_EXCLUSIVE |
| 59 | ); |
| 60 | const advisorTool = useExperimentOverrideValue(EXPERIMENT_IDS.ADVISOR_TOOL); |
| 61 | const execSubagentHardRestart = useExperimentOverrideValue( |
| 62 | EXPERIMENT_IDS.EXEC_SUBAGENT_HARD_RESTART |
| 63 | ); |
| 64 | const dynamicWorkflows = useExperimentOverrideValue(EXPERIMENT_IDS.DYNAMIC_WORKFLOWS); |
| 65 | const memory = useExperimentOverrideValue(EXPERIMENT_IDS.MEMORY); |
| 66 | |
| 67 | // Prefer metadata over the global default until workspace localStorage seeding catches up. |
| 68 | const metadataSettings = getWorkspaceAiSettingsFromMetadata( |
| 69 | workspaceMetadata.get(workspaceId), |
| 70 | agentId |
| 71 | ); |
| 72 | const baseModel = normalizeModelPreference( |
| 73 | preferredModel, |
| 74 | metadataSettings.model ?? defaultModel |
| 75 | ); |
| 76 | |
| 77 | const options = buildSendMessageOptions({ |
| 78 | agentId, |
| 79 | thinkingLevel, |
| 80 | model: baseModel, |
| 81 | providerOptions, |
| 82 | experiments: { |
| 83 | programmaticToolCalling, |
| 84 | programmaticToolCallingExclusive, |
| 85 | advisorTool, |
| 86 | execSubagentHardRestart, |
| 87 | dynamicWorkflows, |