Skip to content

feat(settings): restore agent and model defaults#2723

Draft
arnestrickmann wants to merge 1 commit into
generalaction:mainfrom
arnestrickmann:arne/eng-1687-bring-back-default-agent-and-default-underlying-model
Draft

feat(settings): restore agent and model defaults#2723
arnestrickmann wants to merge 1 commit into
generalaction:mainfrom
arnestrickmann:arne/eng-1687-bring-back-default-agent-and-default-underlying-model

Conversation

@arnestrickmann

@arnestrickmann arnestrickmann commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Restore global default agent and default model settings in the Agents settings tab.
  • Add separate automation defaults for agent and model without changing the automation creation flow.
  • Apply saved defaults when creating tasks, conversations, and automation runs, while preserving per-flow overrides.
  • Keep the model selector slot stable for agents without selectable models and explain the disabled state in a tooltip.

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

CleanShot 2026-06-30 at 12 17 59

Validation

  • corepack pnpm run typecheck
  • corepack pnpm exec vitest run --project node src/main/core/automations/actions/taskCreate.test.ts src/renderer/tests/conversation-provider-selection.test.ts
  • focused oxlint and oxfmt checks

@arnestrickmann arnestrickmann marked this pull request as ready for review June 30, 2026 10:21
@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This 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.

  • Adds defaultModel, defaultAutomationAgent, and defaultAutomationModel app settings (schema + registry), surfaces them in a refactored DefaultAgentSelector component with a new DefaultModelSelect, and threads the values into useEffectiveProvider, useInitialConversationState, CreateConversationModal, and taskCreate.ts.
  • The agent-info-card hover shortcut now also shows an inline model selector when the card belongs to the current default agent.
  • A modelTouched flag guards the pre-fill effect in both useInitialConversationState and CreateConversationModal so user selections are not overwritten by setting changes.

Confidence Score: 3/5

The 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 defaultAutomationModel is configured causes useInitialConversationState to asynchronously update model from null to the default value. That state change trips useAutomationSettingsAutoSave's [provider, model] watcher after the initial-mount guard has cleared, calling savePatch() with no user interaction. Once saved, the automation has an explicit model that no longer tracks future changes to defaultAutomationModel — precisely the fallback in taskCreate.ts this PR is designed to support. The backend logic, new settings, and CreateConversationModal path are clean; the issue is localised to the automation form state interaction.

initial-conversation-section.tsx (model init effect) and useAutomationSettingsAutoSave.ts (dependency on model change) interact in a way that causes silent auto-saves when the form opens.

Important Files Changed

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
Loading
%%{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
Loading
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

Comment on lines +117 to +119
useEffect(() => {
if (!modelTouched) setModel(defaultModel);
}, [defaultModel, modelTouched]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

Comment on lines +93 to +97
const defaultAutomationModel =
provider === defaultAutomationAgent
? await appSettingsService.get('defaultAutomationModel')
: null;
const model = automation.conversationConfig?.model || defaultAutomationModel || undefined;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Comment on lines +62 to +79
<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>
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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!

@arnestrickmann arnestrickmann marked this pull request as draft June 30, 2026 10:34
@arnestrickmann arnestrickmann force-pushed the arne/eng-1687-bring-back-default-agent-and-default-underlying-model branch from fa76d25 to 42cfa49 Compare June 30, 2026 10:56
@arnestrickmann

arnestrickmann commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the review feedback in the amended commit 42cfa49:

  • Automation forms now opt out of applying the runtime automation default model into local form state. Opening an existing automation with no explicit model keeps conversationConfig.model unset, so future runs continue to use the current defaultAutomationModel fallback instead of silently snapshotting it on form open.
  • Existing explicit automation models still seed into the form through the existing seedModel path and can be changed by the user.
  • The disabled no-model placeholder no longer presents itself as an activatable ARIA button.
  • I intentionally kept defaultAutomationModel independent from the global defaultModel; null for the automation default means use the agent default model, not inherit task/conversation defaults. Added a backend comment to make that explicit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant