feat(settings): restore agent and model defaults#2723
Conversation
Greptile SummaryThis PR restores global default agent/model settings in the Agents settings page and introduces separate automation-specific defaults, applying them when creating tasks, conversations, and automation runs while respecting per-automation overrides.
Confidence Score: 3/5The settings schema, execution-time defaults, and UI components are solid, but the automation form's model initialisation path creates an unintended side-effect that silently writes data back into automation configs. Opening an automation's settings page when
|
| Filename | Overview |
|---|---|
| apps/emdash-desktop/src/main/core/automations/actions/taskCreate.ts | Adds defaultAutomationAgent and defaultAutomationModel fallback resolution before creating the conversation. The model guard (provider === defaultAutomationAgent) correctly avoids cross-agent model bleed, but there is no defaultModel fallback in the model chain analogous to the defaultAgent fallback in the agent chain. |
| apps/emdash-desktop/src/main/core/automations/actions/taskCreate.test.ts | Adds two well-targeted test cases: one verifying automation default agent+model are used when the automation has no explicit provider/model, and one verifying that an automation-specific model wins over the default. |
| apps/emdash-desktop/src/renderer/features/tasks/conversations/initial-conversation-section.tsx | Adds modelTouched flag and a useEffect that syncs model from the resolved default. The async initialisation from null → defaultModel can trigger useAutomationSettingsAutoSave's [provider, model] watcher before the user touches the form, causing an unintended auto-save that permanently snaps the current default into the automation config. |
| apps/emdash-desktop/src/renderer/features/automations/useAutomationSettingsAutoSave.ts | Adds model to buildConversationConfig and to the auto-save dependency array [provider, model]. The dependency expansion is correct, but it makes the auto-save sensitive to the model's async initialisation in useInitialConversationState. |
| apps/emdash-desktop/src/renderer/features/tasks/conversations/use-effective-provider.ts | Exposes defaultProviderId and adds an optional defaultAgentSettingKey parameter. Clean, well-typed extension that enables callers to scope to automation-specific defaults. |
| apps/emdash-desktop/src/renderer/features/settings/agents-page/DefaultAgentSelector.tsx | Refactored into a reusable DefaultAgentModelRow and a composed DefaultAgentSelector section with both global and automation-specific agent+model rows. Model is correctly cleared when the agent is changed via the selector. |
| apps/emdash-desktop/src/renderer/features/settings/agents-page/DefaultModelSelect.tsx | New component for selecting a persisted default model. Includes a reset button, stable-key select items, an auto-clear effect for stale saved values, and a disabled placeholder with tooltip. Minor accessibility issue with the disabled placeholder element. |
| apps/emdash-desktop/src/renderer/features/tasks/conversations/create-conversation-modal.tsx | Adds modelTouched state and a useEffect to pre-select the saved defaultModel when the modal opens, resetting on provider change. Correctly scoped to the non-automation create-conversation flow. |
| apps/emdash-desktop/src/main/core/settings/schema.ts | Adds defaultModel, defaultAutomationAgent, and defaultAutomationModel to both schema maps. The new defaultModelSchema is nullable/optional with a null default, which is appropriate. |
| apps/emdash-desktop/src/main/core/settings/settings-registry.ts | Adds runtime defaults for the three new settings, all consistent with the schema defaults. |
| apps/emdash-desktop/src/renderer/lib/components/agent-selector/agent-info-card.tsx | Adds an inline DefaultModelSelect row below the default-agent toggle, visible only when the card's agent is the current default and has selectable models. Correctly bound to defaultModel (global scope). |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
S1[defaultAutomationAgent] -->|fallback| S2[defaultAgent]
S2 -->|fallback| S3[DEFAULT_AGENT_ID]
S3 --> PA[resolved provider]
PA --> MC{provider == defaultAutomationAgent?}
MC -->|yes| FETCH[fetch defaultAutomationModel]
MC -->|no| NULLM[model = null]
FETCH --> MC2{automation has explicit model?}
MC2 -->|yes| USE_EXP[use explicit model]
MC2 -->|no| USE_DEF[use defaultAutomationModel]
subgraph Form [Automation Settings Form]
F1[useInitialConversationState] -->|useEffect sets model from defaultAutomationModel| F2[model state changes]
F2 --> F3[useAutomationSettingsAutoSave detects change]
F3 -->|canSave| F4[savePatch persists model into conversationConfig]
end
F4 -.->|next run: explicit model now set| USE_EXP
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
S1[defaultAutomationAgent] -->|fallback| S2[defaultAgent]
S2 -->|fallback| S3[DEFAULT_AGENT_ID]
S3 --> PA[resolved provider]
PA --> MC{provider == defaultAutomationAgent?}
MC -->|yes| FETCH[fetch defaultAutomationModel]
MC -->|no| NULLM[model = null]
FETCH --> MC2{automation has explicit model?}
MC2 -->|yes| USE_EXP[use explicit model]
MC2 -->|no| USE_DEF[use defaultAutomationModel]
subgraph Form [Automation Settings Form]
F1[useInitialConversationState] -->|useEffect sets model from defaultAutomationModel| F2[model state changes]
F2 --> F3[useAutomationSettingsAutoSave detects change]
F3 -->|canSave| F4[savePatch persists model into conversationConfig]
end
F4 -.->|next run: explicit model now set| USE_EXP
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 3
apps/emdash-desktop/src/renderer/features/tasks/conversations/initial-conversation-section.tsx:117-119
**Model initialization triggers automation auto-save on form open**
`useInitialConversationState` starts with `model = null` and then asynchronously updates it to `defaultModel` via this effect. In `useAutomationSettingsAutoSave`, the dependency array `[provider, model]` means any post-mount change to `model` calls `savePatch()`. If a user opens the automation settings page when `defaultAutomationModel` is non-null, the model initialises from `null` → default, triggering an auto-save that permanently snapshots the current default into `conversationConfig.model`. Future changes to `defaultAutomationModel` won't affect this automation because it now has an explicit value. Additionally, since `useInitialConversationState` has no `seedModel`/`initialModel` parameter, any pre-existing `conversationConfig.model` (saved by a prior form open) is silently ignored and cannot be displayed or corrected by the user.
### Issue 2 of 3
apps/emdash-desktop/src/main/core/automations/actions/taskCreate.ts:93-97
**`defaultAutomationModel` is not applied when the automation-specific provider overrides to a different agent than the default**
`defaultAutomationModel` is gated on `provider === defaultAutomationAgent`, which correctly avoids applying a model intended for agent A to agent B. However, the agent-level fallback chain is `defaultAutomationAgent → defaultAgent → DEFAULT_AGENT_ID`, but there is no corresponding model fallback (`defaultAutomationModel → defaultModel`). A user who configured `defaultModel` but not `defaultAutomationModel` will find that the global model default does not propagate to automation runs, even when the automation agent falls back to `defaultAgent`. This asymmetry is at minimum worth a comment and may warrant a `defaultModel` fallback in the chain.
### Issue 3 of 3
apps/emdash-desktop/src/renderer/features/settings/agents-page/DefaultModelSelect.tsx:62-79
**Disabled select placeholder is keyboard-activatable without doing anything**
The no-model-options path renders a `div` with `role="button"` and `tabIndex={0}`, but has no `onKeyDown` handler. A keyboard user tabbing to it and pressing Enter/Space gets no feedback — the tooltip only appears on hover/focus, and activation has no effect. Consider either removing `tabIndex={0}` (since the element is truly inactive) or adding an `onKeyDown` to present the tooltip programmatically, consistent with ARIA button expectations.
Reviews (1): Last reviewed commit: "feat(settings): restore agent and model ..." | Re-trigger Greptile
| useEffect(() => { | ||
| if (!modelTouched) setModel(defaultModel); | ||
| }, [defaultModel, modelTouched]); |
There was a problem hiding this comment.
Model initialization triggers automation auto-save on form open
useInitialConversationState starts with model = null and then asynchronously updates it to defaultModel via this effect. In useAutomationSettingsAutoSave, the dependency array [provider, model] means any post-mount change to model calls savePatch(). If a user opens the automation settings page when defaultAutomationModel is non-null, the model initialises from null → default, triggering an auto-save that permanently snapshots the current default into conversationConfig.model. Future changes to defaultAutomationModel won't affect this automation because it now has an explicit value. Additionally, since useInitialConversationState has no seedModel/initialModel parameter, any pre-existing conversationConfig.model (saved by a prior form open) is silently ignored and cannot be displayed or corrected by the user.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/emdash-desktop/src/renderer/features/tasks/conversations/initial-conversation-section.tsx
Line: 117-119
Comment:
**Model initialization triggers automation auto-save on form open**
`useInitialConversationState` starts with `model = null` and then asynchronously updates it to `defaultModel` via this effect. In `useAutomationSettingsAutoSave`, the dependency array `[provider, model]` means any post-mount change to `model` calls `savePatch()`. If a user opens the automation settings page when `defaultAutomationModel` is non-null, the model initialises from `null` → default, triggering an auto-save that permanently snapshots the current default into `conversationConfig.model`. Future changes to `defaultAutomationModel` won't affect this automation because it now has an explicit value. Additionally, since `useInitialConversationState` has no `seedModel`/`initialModel` parameter, any pre-existing `conversationConfig.model` (saved by a prior form open) is silently ignored and cannot be displayed or corrected by the user.
How can I resolve this? If you propose a fix, please make it concise.| const defaultAutomationModel = | ||
| provider === defaultAutomationAgent | ||
| ? await appSettingsService.get('defaultAutomationModel') | ||
| : null; | ||
| const model = automation.conversationConfig?.model || defaultAutomationModel || undefined; |
There was a problem hiding this comment.
defaultAutomationModel is not applied when the automation-specific provider overrides to a different agent than the default
defaultAutomationModel is gated on provider === defaultAutomationAgent, which correctly avoids applying a model intended for agent A to agent B. However, the agent-level fallback chain is defaultAutomationAgent → defaultAgent → DEFAULT_AGENT_ID, but there is no corresponding model fallback (defaultAutomationModel → defaultModel). A user who configured defaultModel but not defaultAutomationModel will find that the global model default does not propagate to automation runs, even when the automation agent falls back to defaultAgent. This asymmetry is at minimum worth a comment and may warrant a defaultModel fallback in the chain.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/emdash-desktop/src/main/core/automations/actions/taskCreate.ts
Line: 93-97
Comment:
**`defaultAutomationModel` is not applied when the automation-specific provider overrides to a different agent than the default**
`defaultAutomationModel` is gated on `provider === defaultAutomationAgent`, which correctly avoids applying a model intended for agent A to agent B. However, the agent-level fallback chain is `defaultAutomationAgent → defaultAgent → DEFAULT_AGENT_ID`, but there is no corresponding model fallback (`defaultAutomationModel → defaultModel`). A user who configured `defaultModel` but not `defaultAutomationModel` will find that the global model default does not propagate to automation runs, even when the automation agent falls back to `defaultAgent`. This asymmetry is at minimum worth a comment and may warrant a `defaultModel` fallback in the chain.
How can I resolve this? If you propose a fix, please make it concise.| <TooltipProvider delay={150}> | ||
| <Tooltip> | ||
| <TooltipTrigger> | ||
| <div | ||
| aria-disabled="true" | ||
| role="button" | ||
| tabIndex={0} | ||
| className="flex h-8 w-44 cursor-not-allowed items-center justify-between gap-1.5 rounded-md border border-border bg-transparent px-2.5 py-1 text-sm text-foreground-muted opacity-50 outline-none" | ||
| > | ||
| <span className="truncate">Default model</span> | ||
| <ChevronDownIcon className="size-4 shrink-0" /> | ||
| </div> | ||
| </TooltipTrigger> | ||
| <TooltipContent side="top">This agent does not support model selection.</TooltipContent> | ||
| </Tooltip> | ||
| </TooltipProvider> | ||
| </div> | ||
| ); |
There was a problem hiding this comment.
Disabled select placeholder is keyboard-activatable without doing anything
The no-model-options path renders a div with role="button" and tabIndex={0}, but has no onKeyDown handler. A keyboard user tabbing to it and pressing Enter/Space gets no feedback — the tooltip only appears on hover/focus, and activation has no effect. Consider either removing tabIndex={0} (since the element is truly inactive) or adding an onKeyDown to present the tooltip programmatically, consistent with ARIA button expectations.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/emdash-desktop/src/renderer/features/settings/agents-page/DefaultModelSelect.tsx
Line: 62-79
Comment:
**Disabled select placeholder is keyboard-activatable without doing anything**
The no-model-options path renders a `div` with `role="button"` and `tabIndex={0}`, but has no `onKeyDown` handler. A keyboard user tabbing to it and pressing Enter/Space gets no feedback — the tooltip only appears on hover/focus, and activation has no effect. Consider either removing `tabIndex={0}` (since the element is truly inactive) or adding an `onKeyDown` to present the tooltip programmatically, consistent with ARIA button expectations.
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
fa76d25 to
42cfa49
Compare
|
Addressed the review feedback in the amended commit 42cfa49:
|
Summary
Why
Automations and repeated task creation need predictable defaults for both the harness and the underlying model. This brings those settings back in the central Agents settings page and keeps the hover-card shortcut aligned with the same app setting.
Linear: https://linear.app/general-action/issue/ENG-1687/bring-back-default-agent-and-default-underlying-model-settings
Screenshot
Validation