From 84206f6a38cdbd5c83d10f48804831390a32363a Mon Sep 17 00:00:00 2001
From: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex
 <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 16:52:30 -0700
Subject: [PATCH 01/17] Redesign agent AI configuration

Co-authored-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
---
 .../agents/ui/AgentAiConfigurationMode.tsx    |  38 +++++
 .../agents/ui/AgentDefinitionDialog.tsx       | 135 ++++++++++++------
 .../ui/agentAiConfigurationPolicy.test.mjs    |  73 ++++++++++
 .../agents/ui/agentAiConfigurationPolicy.ts   |  41 ++++++
 .../ui/agentDefinitionSubmitPayload.test.mjs  |   8 +-
 5 files changed, 251 insertions(+), 44 deletions(-)
 create mode 100644 desktop/src/features/agents/ui/AgentAiConfigurationMode.tsx
 create mode 100644 desktop/src/features/agents/ui/agentAiConfigurationPolicy.test.mjs
 create mode 100644 desktop/src/features/agents/ui/agentAiConfigurationPolicy.ts

diff --git a/desktop/src/features/agents/ui/AgentAiConfigurationMode.tsx b/desktop/src/features/agents/ui/AgentAiConfigurationMode.tsx
new file mode 100644
index 0000000000..33b7ddd12f
--- /dev/null
+++ b/desktop/src/features/agents/ui/AgentAiConfigurationMode.tsx
@@ -0,0 +1,38 @@
+import { Tabs, TabsList, TabsTrigger } from "@/shared/ui/tabs";
+import type { AgentAiConfigurationMode } from "./agentAiConfigurationPolicy";
+
+export type { AgentAiConfigurationMode } from "./agentAiConfigurationPolicy";
+
+export function AgentAiConfigurationModeField({
+  defaultsSummary,
+  mode,
+  onModeChange,
+}: {
+  defaultsSummary: string;
+  mode: AgentAiConfigurationMode;
+  onModeChange: (mode: AgentAiConfigurationMode) => void;
+}) {
+  return (
+    <div className="space-y-1.5">
+      <p className="text-sm font-medium text-foreground">AI configuration</p>
+      <Tabs
+        onValueChange={(value) =>
+          onModeChange(value as AgentAiConfigurationMode)
+        }
+        value={mode}
+      >
+        <TabsList>
+          <TabsTrigger value="defaults">Use AI defaults</TabsTrigger>
+          <TabsTrigger value="custom">Customize for this agent</TabsTrigger>
+        </TabsList>
+      </Tabs>
+      <p className="text-xs text-muted-foreground">
+        {mode === "defaults"
+          ? defaultsSummary === "Not configured"
+            ? "AI defaults aren’t configured yet."
+            : `${defaultsSummary}. Future default changes apply automatically.`
+          : "Provider and model changes apply only to this agent."}
+      </p>
+    </div>
+  );
+}
diff --git a/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx b/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx
index 2e0582e07d..e5e18a2dc6 100644
--- a/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx
+++ b/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx
@@ -38,13 +38,11 @@ import {
   CUSTOM_PROVIDER_DROPDOWN_VALUE,
   computeLocalModeGate,
   formatRuntimeOptionLabel,
-  getDefaultLlmProviderLabel,
   getDefaultPersonaRuntime,
   getPersonaModelOptions,
   getPersonaProviderOptions,
   getRuntimePersonaModelOptions,
   NO_RUNTIME_DROPDOWN_VALUE,
-  providerRequiresExplicitModel,
   runtimeSupportsLlmProviderSelection,
   type PersonaDropdownOption,
   PERSONA_FIELD_CONTROL_CLASS,
@@ -69,12 +67,20 @@ import {
   usePersonaModelDiscovery,
 } from "./usePersonaModelDiscovery";
 import { useBakedBuildEnvKeysQuery, useRuntimeFileConfigQuery } from "../hooks";
-import {
-  getBakedModelInheritLabel,
-  getBakedProviderInheritLabel,
-} from "./bakedEnvHelpers";
 import { useAgentDialogDefaults } from "./useAgentDialogDefaults";
-import { AgentAiDefaultsNotice } from "./AgentAiDefaults";
+import {
+  AgentAiDefaultsNotice,
+  formatAiDefaultsSummary,
+} from "./AgentAiDefaults";
+import {
+  AgentAiConfigurationModeField,
+  type AgentAiConfigurationMode,
+} from "./AgentAiConfigurationMode";
+import {
+  agentAiConfigurationModeSatisfied,
+  agentAiConfigurationPairForMode,
+  initialAgentAiConfigurationMode,
+} from "./agentAiConfigurationPolicy";
 import { useProviderApiKeyFieldState } from "./providerApiKeyFieldState";
 import { buildRuntimeModelProviderPayload } from "./agentDefinitionSubmitPayload";
 
@@ -125,6 +131,8 @@ export function AgentDefinitionDialog({
   const [model, setModel] = React.useState("");
   const [isCustomModelEditing, setIsCustomModelEditing] = React.useState(false);
   const [provider, setProvider] = React.useState("");
+  const [aiConfigurationMode, setAiConfigurationMode] =
+    React.useState<AgentAiConfigurationMode>("defaults");
   const [isCustomProviderEditing, setIsCustomProviderEditing] =
     React.useState(false);
   const [namePoolText, setNamePoolText] = React.useState("");
@@ -172,6 +180,12 @@ export function AgentDefinitionDialog({
     setModel(initialValues.model ?? "");
     setIsCustomModelEditing(false);
     setProvider(initialValues.provider ?? "");
+    setAiConfigurationMode(
+      initialAgentAiConfigurationMode({
+        provider: initialValues.provider ?? "",
+        model: initialValues.model ?? "",
+      }),
+    );
     setIsCustomProviderEditing(false);
     const nextNamePoolText =
       "namePool" in initialValues
@@ -226,6 +240,7 @@ export function AgentDefinitionDialog({
       setModel("");
       setIsCustomModelEditing(false);
       setProvider("");
+      setAiConfigurationMode("defaults");
       setIsCustomProviderEditing(false);
       setNamePoolText("");
       setEnvVars({});
@@ -251,8 +266,8 @@ export function AgentDefinitionDialog({
       provider: providerForSubmit,
     } = buildRuntimeModelProviderPayload({
       runtime,
-      model,
-      provider,
+      model: aiConfigurationMode === "defaults" ? "" : model,
+      provider: aiConfigurationMode === "defaults" ? "" : provider,
       isEditMode: "id" in initialValues,
       isAutoSeeded: isRuntimeAutoSeededRef.current,
       initialPreviousRuntime: initialValues.runtime?.trim() ?? "",
@@ -325,6 +340,26 @@ export function AgentDefinitionDialog({
     },
     inheritedEnvVars: inheritedEnvVarsForAdvanced,
   } = useAgentDialogDefaults({ open });
+  const aiDefaultsSummary = formatAiDefaultsSummary({
+    provider: inheritedProviderDefault,
+    model: inheritedModelDefault,
+  });
+
+  function handleAiConfigurationModeChange(nextMode: AgentAiConfigurationMode) {
+    setAiConfigurationMode(nextMode);
+    setIsCustomProviderEditing(false);
+    setIsCustomModelEditing(false);
+    const nextPair = agentAiConfigurationPairForMode({
+      current: { provider, model },
+      inherited: {
+        provider: inheritedProviderDefault.value,
+        model: inheritedModelDefault.value,
+      },
+      mode: nextMode,
+    });
+    setProvider(nextPair.provider);
+    setModel(nextPair.model);
+  }
   const { data: bakedEnvKeys, isLoading: bakedLoading } =
     useBakedBuildEnvKeysQuery({ enabled: open });
   const credentialSettled = !fileConfigLoading && !bakedLoading;
@@ -391,13 +426,18 @@ export function AgentDefinitionDialog({
   // missingNormalizedFields check would flip the asterisk off once a value is
   // selected, which is incoherent (required means required, not "required until
   // satisfied"). runtimeSupportsLlmProviderSelection is the authoritative gate.
-  const providerIsRequired = runtimeSupportsLlmProviderSelection(runtime);
+  const providerIsRequired =
+    aiConfigurationMode === "custom" &&
+    runtimeSupportsLlmProviderSelection(runtime);
   const modelFieldVisible =
     runtime.trim().length > 0 || blankRuntimeModelProviderEditable;
-  // Static asterisk on the model label: uses effectiveProvider so a globally-
-  // set provider correctly marks the model field required.
-  const isExplicitModelRequired =
-    modelFieldVisible && providerRequiresExplicitModel(effectiveProvider);
+  // Customize pins a complete provider/model pair. Shared compute's concrete
+  // automatic-routing value is the only valid non-model-id choice.
+  const isExplicitModelRequired = aiConfigurationMode === "custom";
+  const customAiPairSatisfied = agentAiConfigurationModeSatisfied(
+    aiConfigurationMode,
+    { provider, model },
+  );
   const isCreateMode = Boolean(initialValues && !("id" in initialValues));
   const selectedRuntimeIsAvailable =
     runtime.trim().length === 0 ||
@@ -415,6 +455,7 @@ export function AgentDefinitionDialog({
     // D1: localModeSatisfied covers both missingNormalizedFields AND
     // missingEnvKeys — credential env keys now block submit, not just display.
     localModeSatisfied &&
+    customAiPairSatisfied &&
     !isAvatarUploadPending;
 
   // Merge global env as the base layer so credential keys satisfied via global
@@ -476,13 +517,6 @@ export function AgentDefinitionDialog({
       : "",
     hideProviderIds,
   );
-  const defaultLlmProviderLabel =
-    inheritedProviderDefault.source === "build"
-      ? getBakedProviderInheritLabel(
-          inheritedProviderDefault.value,
-          providerOptions,
-        )
-      : getDefaultLlmProviderLabel(runtime, inheritedProviderDefault.value);
   const providerSelectValue = isCustomProviderEditing
     ? CUSTOM_PROVIDER_DROPDOWN_VALUE
     : trimmedProvider || AUTO_PROVIDER_DROPDOWN_VALUE;
@@ -525,25 +559,30 @@ export function AgentDefinitionDialog({
     });
   }
   const providerDropdownOptions: PersonaDropdownOption[] = [
-    ...providerOptions.map((option) => ({
-      label: option.id === "" ? defaultLlmProviderLabel : option.label,
-      value: option.id || AUTO_PROVIDER_DROPDOWN_VALUE,
-    })),
+    ...providerOptions
+      .filter((option) => option.id.trim().length > 0)
+      .map((option) => ({
+        label: option.label,
+        value: option.id,
+      })),
     { label: "Custom provider...", value: CUSTOM_PROVIDER_DROPDOWN_VALUE },
   ];
-  const inheritedModelLabel =
-    inheritedModelDefault.source === "build"
-      ? getBakedModelInheritLabel(inheritedModelDefault.value)
-      : undefined;
   const modelDropdownOptions: PersonaDropdownOption[] =
     buildModelDropdownOptions({
       allowCustom: !isRelayMesh,
-      globalModel: isRelayMesh ? undefined : inheritedModelDefault.value,
-      globalModelLabel: isRelayMesh ? undefined : inheritedModelLabel,
+      globalModel: undefined,
       loading: modelDiscoveryLoading && discoveredModelOptions === null,
       loadingValue: MODEL_DISCOVERY_LOADING_VALUE,
       options: modelOptions,
-    });
+    })
+      .filter(
+        (option) => isRelayMesh || option.value !== AUTO_MODEL_DROPDOWN_VALUE,
+      )
+      .map((option) =>
+        isRelayMesh && option.value === AUTO_MODEL_DROPDOWN_VALUE
+          ? { ...option, label: "Automatic" }
+          : option,
+      );
   const previewLabel = displayName.trim() || "Agent name";
   const previewAvatarUrl = avatarUrl.trim() || null;
   const runtimeWarning =
@@ -778,6 +817,14 @@ export function AgentDefinitionDialog({
             </div>
 
             {llmProviderFieldVisible ? (
+              <AgentAiConfigurationModeField
+                defaultsSummary={aiDefaultsSummary}
+                mode={aiConfigurationMode}
+                onModeChange={handleAiConfigurationModeChange}
+              />
+            ) : null}
+
+            {llmProviderFieldVisible && aiConfigurationMode === "custom" ? (
               <div className="space-y-1.5">
                 <RequiredFieldLabel
                   htmlFor="persona-llm-provider"
@@ -795,7 +842,7 @@ export function AgentDefinitionDialog({
                   id="persona-llm-provider"
                   onValueChange={handleProviderDropdownChange}
                   options={providerDropdownOptions}
-                  placeholder={defaultLlmProviderLabel}
+                  placeholder="Choose a provider"
                   value={providerSelectValue}
                 />
                 {showCustomProviderInput ? (
@@ -823,7 +870,9 @@ export function AgentDefinitionDialog({
               </div>
             ) : null}
 
-            {llmProviderFieldVisible && topLevelSecretEnvVar ? (
+            {llmProviderFieldVisible &&
+            aiConfigurationMode === "custom" &&
+            topLevelSecretEnvVar ? (
               <PersonaProviderApiKeyField
                 disabled={isPending}
                 isInherited={apiKeyIsInherited}
@@ -845,7 +894,7 @@ export function AgentDefinitionDialog({
             ) : null}
 
             <AnimatePresence initial={false}>
-              {modelFieldVisible ? (
+              {modelFieldVisible && aiConfigurationMode === "custom" ? (
                 <PersonaModelField
                   disabled={isPending}
                   isExplicitModelRequired={isExplicitModelRequired}
@@ -865,13 +914,15 @@ export function AgentDefinitionDialog({
               ) : null}
             </AnimatePresence>
 
-            <AgentAiDefaultsNotice
-              confirmNavigation
-              explicitModel={model}
-              explicitProvider={provider}
-              inheritedModel={inheritedModelDefault}
-              inheritedProvider={inheritedProviderDefault}
-            />
+            {aiConfigurationMode === "defaults" ? (
+              <AgentAiDefaultsNotice
+                confirmNavigation
+                explicitModel=""
+                explicitProvider=""
+                inheritedModel={inheritedModelDefault}
+                inheritedProvider={inheritedProviderDefault}
+              />
+            ) : null}
 
             {isCreateMode ? createRunSection : null}
 
diff --git a/desktop/src/features/agents/ui/agentAiConfigurationPolicy.test.mjs b/desktop/src/features/agents/ui/agentAiConfigurationPolicy.test.mjs
new file mode 100644
index 0000000000..6f33752952
--- /dev/null
+++ b/desktop/src/features/agents/ui/agentAiConfigurationPolicy.test.mjs
@@ -0,0 +1,73 @@
+import assert from "node:assert/strict";
+import test from "node:test";
+
+import {
+  agentAiConfigurationModeSatisfied,
+  agentAiConfigurationPairForMode,
+  initialAgentAiConfigurationMode,
+} from "./agentAiConfigurationPolicy.ts";
+
+test("existing one-sided and complete overrides open in Customize", () => {
+  assert.equal(
+    initialAgentAiConfigurationMode({ provider: "anthropic" }),
+    "custom",
+  );
+  assert.equal(
+    initialAgentAiConfigurationMode({ model: "claude-opus" }),
+    "custom",
+  );
+  assert.equal(
+    initialAgentAiConfigurationMode({
+      provider: "anthropic",
+      model: "claude-opus",
+    }),
+    "custom",
+  );
+  assert.equal(initialAgentAiConfigurationMode({}), "defaults");
+});
+
+test("Customize requires a complete explicit pair", () => {
+  assert.equal(
+    agentAiConfigurationModeSatisfied("custom", {
+      provider: "anthropic",
+      model: "",
+    }),
+    false,
+  );
+  assert.equal(
+    agentAiConfigurationModeSatisfied("custom", {
+      provider: "",
+      model: "claude-opus",
+    }),
+    false,
+  );
+  assert.equal(
+    agentAiConfigurationModeSatisfied("custom", {
+      provider: "anthropic",
+      model: "claude-opus",
+    }),
+    true,
+  );
+});
+
+test("Defaults clears provider and model together", () => {
+  assert.deepEqual(
+    agentAiConfigurationPairForMode({
+      current: { provider: "anthropic", model: "claude-opus" },
+      inherited: { provider: "databricks_v2", model: "llama" },
+      mode: "defaults",
+    }),
+    { provider: "", model: "" },
+  );
+});
+
+test("entering Customize pins unresolved fields from the inherited pair", () => {
+  assert.deepEqual(
+    agentAiConfigurationPairForMode({
+      current: { provider: "anthropic", model: "" },
+      inherited: { provider: "databricks_v2", model: "llama" },
+      mode: "custom",
+    }),
+    { provider: "anthropic", model: "llama" },
+  );
+});
diff --git a/desktop/src/features/agents/ui/agentAiConfigurationPolicy.ts b/desktop/src/features/agents/ui/agentAiConfigurationPolicy.ts
new file mode 100644
index 0000000000..f89d4022cc
--- /dev/null
+++ b/desktop/src/features/agents/ui/agentAiConfigurationPolicy.ts
@@ -0,0 +1,41 @@
+export type AgentAiConfigurationMode = "defaults" | "custom";
+
+export type AgentAiConfigurationPair = {
+  provider: string;
+  model: string;
+};
+
+export function initialAgentAiConfigurationMode(
+  pair: Partial<AgentAiConfigurationPair>,
+): AgentAiConfigurationMode {
+  return pair.provider?.trim() || pair.model?.trim() ? "custom" : "defaults";
+}
+
+export function agentAiConfigurationPairForMode({
+  current,
+  inherited,
+  mode,
+}: {
+  current: AgentAiConfigurationPair;
+  inherited: AgentAiConfigurationPair;
+  mode: AgentAiConfigurationMode;
+}): AgentAiConfigurationPair {
+  if (mode === "defaults") {
+    return { provider: "", model: "" };
+  }
+
+  return {
+    provider: current.provider.trim() || inherited.provider,
+    model: current.model.trim() || inherited.model,
+  };
+}
+
+export function agentAiConfigurationModeSatisfied(
+  mode: AgentAiConfigurationMode,
+  pair: AgentAiConfigurationPair,
+) {
+  return (
+    mode === "defaults" ||
+    (pair.provider.trim().length > 0 && pair.model.trim().length > 0)
+  );
+}
diff --git a/desktop/src/features/agents/ui/agentDefinitionSubmitPayload.test.mjs b/desktop/src/features/agents/ui/agentDefinitionSubmitPayload.test.mjs
index 9cf4654650..87d5b81b7d 100644
--- a/desktop/src/features/agents/ui/agentDefinitionSubmitPayload.test.mjs
+++ b/desktop/src/features/agents/ui/agentDefinitionSubmitPayload.test.mjs
@@ -33,7 +33,9 @@ test("edit-untouched: model and provider omitted when user changes nothing on au
 
 // ── edit-model-only ────────────────────────────────────────────────────────────
 //
-// User opens a null-runtime builtin with auto-seed and picks a model.
+// Transport-level coverage: the serializer remains permissive for legacy and
+// non-dialog callers. The separately tested AI configuration mode policy blocks
+// a model-only Customize submission in AgentDefinitionDialog.
 // Expected: model persisted, runtime omitted (auto-seeded, not explicit).
 
 test("edit-model-only: chosen model persists, runtime omitted on auto-seeded builtin", () => {
@@ -51,7 +53,9 @@ test("edit-model-only: chosen model persists, runtime omitted on auto-seeded bui
 
 // ── edit-provider-only ─────────────────────────────────────────────────────────
 //
-// User opens a null-runtime builtin with auto-seed and picks a provider.
+// Transport-level coverage: the serializer remains permissive for legacy and
+// non-dialog callers. The separately tested AI configuration mode policy blocks
+// a provider-only Customize submission in AgentDefinitionDialog.
 // Expected: provider persisted, model and runtime omitted.
 
 test("edit-provider-only: chosen provider persists, runtime omitted on auto-seeded builtin", () => {

From 9f90552924794f240b9e202d75bcccce291f5db9 Mon Sep 17 00:00:00 2001
From: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex
 <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 17:30:53 -0700
Subject: [PATCH 02/17] Add inline agent AI defaults editor

Co-authored-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
---
 .../features/agents/ui/AgentAiDefaults.tsx    |  23 +-
 .../agents/ui/AgentAiDefaultsDialog.tsx       | 141 +++++++++++
 .../agents/ui/AgentDefinitionDialog.tsx       |  12 +-
 .../agents/ui/AgentInstanceEditDialog.tsx     |  12 +-
 .../agents/ui/GlobalAgentConfigEditor.tsx     | 226 ++++++++++++++++++
 .../ui/GlobalAgentConfigSettingsCard.tsx      | 188 +--------------
 6 files changed, 399 insertions(+), 203 deletions(-)
 create mode 100644 desktop/src/features/agents/ui/AgentAiDefaultsDialog.tsx
 create mode 100644 desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx

diff --git a/desktop/src/features/agents/ui/AgentAiDefaults.tsx b/desktop/src/features/agents/ui/AgentAiDefaults.tsx
index 694841d5ed..bf37de5044 100644
--- a/desktop/src/features/agents/ui/AgentAiDefaults.tsx
+++ b/desktop/src/features/agents/ui/AgentAiDefaults.tsx
@@ -1,4 +1,4 @@
-import { useAppNavigation } from "@/app/navigation/useAppNavigation";
+import type * as React from "react";
 import type { InheritedDefault } from "./bakedEnvHelpers";
 import { getPersonaProviderOptions } from "./personaDialogPickers";
 import { Button } from "@/shared/ui/button";
@@ -26,19 +26,20 @@ export function formatAiDefaultsSummary({
 }
 
 export function AgentAiDefaultsNotice({
-  confirmNavigation = false,
+  onEditDefaults,
+  triggerRef,
   explicitModel,
   explicitProvider,
   inheritedModel,
   inheritedProvider,
 }: {
-  confirmNavigation?: boolean;
+  onEditDefaults: () => void;
+  triggerRef?: React.Ref<HTMLButtonElement>;
   explicitModel: string;
   explicitProvider: string;
   inheritedModel: InheritedDefault;
   inheritedProvider: InheritedDefault;
 }) {
-  const { goSettings } = useAppNavigation();
   const inheritsProvider = explicitProvider.trim().length === 0;
   const inheritsModel = explicitModel.trim().length === 0;
 
@@ -82,17 +83,9 @@ export function AgentAiDefaultsNotice({
         </p>
       </div>
       <Button
-        onClick={() => {
-          if (
-            confirmNavigation &&
-            !window.confirm(
-              "Leave this agent without saving? Your changes will be discarded.",
-            )
-          ) {
-            return;
-          }
-          void goSettings("agents");
-        }}
+        onClick={onEditDefaults}
+        ref={triggerRef}
+        data-testid="edit-ai-defaults"
         size="xs"
         type="button"
         variant="link"
diff --git a/desktop/src/features/agents/ui/AgentAiDefaultsDialog.tsx b/desktop/src/features/agents/ui/AgentAiDefaultsDialog.tsx
new file mode 100644
index 0000000000..25b82cc47e
--- /dev/null
+++ b/desktop/src/features/agents/ui/AgentAiDefaultsDialog.tsx
@@ -0,0 +1,141 @@
+import * as React from "react";
+
+import {
+  GlobalAgentConfigEditor,
+  type GlobalAgentConfigSaveResult,
+} from "./GlobalAgentConfigEditor";
+import {
+  AlertDialog,
+  AlertDialogAction,
+  AlertDialogCancel,
+  AlertDialogContent,
+  AlertDialogDescription,
+  AlertDialogFooter,
+  AlertDialogHeader,
+  AlertDialogTitle,
+} from "@/shared/ui/alert-dialog";
+import { Button } from "@/shared/ui/button";
+import {
+  Dialog,
+  DialogContent,
+  DialogDescription,
+  DialogHeader,
+  DialogTitle,
+} from "@/shared/ui/dialog";
+
+export function AgentAiDefaultsDialog({
+  open,
+  onOpenChange,
+  returnFocusRef,
+}: {
+  open: boolean;
+  onOpenChange: (open: boolean) => void;
+  returnFocusRef: React.RefObject<HTMLButtonElement | null>;
+}) {
+  const [dirty, setDirty] = React.useState(false);
+  const [saving, setSaving] = React.useState(false);
+  const [confirmDiscard, setConfirmDiscard] = React.useState(false);
+  const [restartFailures, setRestartFailures] = React.useState(0);
+
+  const closeAndRestoreFocus = React.useCallback(() => {
+    onOpenChange(false);
+    requestAnimationFrame(() => returnFocusRef.current?.focus());
+  }, [onOpenChange, returnFocusRef]);
+
+  const requestClose = React.useCallback(() => {
+    if (saving) return;
+    if (dirty) {
+      setConfirmDiscard(true);
+      return;
+    }
+    closeAndRestoreFocus();
+  }, [closeAndRestoreFocus, dirty, saving]);
+
+  function handleSaveSuccess(result: GlobalAgentConfigSaveResult) {
+    if (result.failed_restart_count > 0) {
+      setRestartFailures(result.failed_restart_count);
+      return;
+    }
+    closeAndRestoreFocus();
+  }
+
+  React.useEffect(() => {
+    if (open) {
+      setDirty(false);
+      setConfirmDiscard(false);
+      setRestartFailures(0);
+    }
+  }, [open]);
+
+  return (
+    <>
+      <Dialog
+        open={open}
+        onOpenChange={(nextOpen) => {
+          if (!nextOpen) requestClose();
+        }}
+      >
+        <DialogContent
+          className="max-h-[calc(100vh-2rem)] max-w-2xl overflow-y-auto"
+          data-testid="agent-ai-defaults-dialog"
+          onCloseAutoFocus={(event) => {
+            event.preventDefault();
+            returnFocusRef.current?.focus();
+          }}
+        >
+          <DialogHeader>
+            <DialogTitle>AI defaults</DialogTitle>
+            <DialogDescription>
+              These settings are inherited by agents using AI defaults. Saving
+              may restart affected running agents.
+            </DialogDescription>
+          </DialogHeader>
+          <GlobalAgentConfigEditor
+            onDirtyChange={setDirty}
+            onSaveSuccess={handleSaveSuccess}
+            onSavingChange={setSaving}
+            secondaryAction={
+              <Button
+                disabled={saving}
+                onClick={
+                  restartFailures > 0 ? closeAndRestoreFocus : requestClose
+                }
+                size="sm"
+                type="button"
+                variant="outline"
+              >
+                {restartFailures > 0 ? "Done" : "Cancel"}
+              </Button>
+            }
+          />
+        </DialogContent>
+      </Dialog>
+
+      <AlertDialog open={confirmDiscard} onOpenChange={setConfirmDiscard}>
+        <AlertDialogContent data-testid="discard-ai-defaults-dialog">
+          <AlertDialogHeader>
+            <AlertDialogTitle>Discard changes to AI defaults?</AlertDialogTitle>
+            <AlertDialogDescription>
+              Your agent draft will stay open, but changes made in this overlay
+              will be lost.
+            </AlertDialogDescription>
+          </AlertDialogHeader>
+          <AlertDialogFooter>
+            <AlertDialogCancel>Keep editing</AlertDialogCancel>
+            <AlertDialogAction asChild>
+              <Button
+                onClick={() => {
+                  setConfirmDiscard(false);
+                  closeAndRestoreFocus();
+                }}
+                variant="destructive"
+              >
+                Discard changes
+              </Button>
+            </AlertDialogAction>
+          </AlertDialogFooter>
+        </AlertDialogContent>
+      </AlertDialog>
+    </>
+  );
+}
diff --git a/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx b/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx
index e5e18a2dc6..7c2d838414 100644
--- a/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx
+++ b/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx
@@ -72,6 +72,7 @@ import {
   AgentAiDefaultsNotice,
   formatAiDefaultsSummary,
 } from "./AgentAiDefaults";
+import { AgentAiDefaultsDialog } from "./AgentAiDefaultsDialog";
 import {
   AgentAiConfigurationModeField,
   type AgentAiConfigurationMode,
@@ -125,6 +126,8 @@ export function AgentDefinitionDialog({
   createSubmitBlocked = false,
 }: AgentDefinitionDialogProps) {
   const [displayName, setDisplayName] = React.useState("");
+  const [aiDefaultsOpen, setAiDefaultsOpen] = React.useState(false);
+  const aiDefaultsTriggerRef = React.useRef<HTMLButtonElement>(null);
   const [avatarUrl, setAvatarUrl] = React.useState("");
   const [systemPrompt, setSystemPrompt] = React.useState("");
   const [runtime, setRuntime] = React.useState("");
@@ -916,7 +919,8 @@ export function AgentDefinitionDialog({
 
             {aiConfigurationMode === "defaults" ? (
               <AgentAiDefaultsNotice
-                confirmNavigation
+                onEditDefaults={() => setAiDefaultsOpen(true)}
+                triggerRef={aiDefaultsTriggerRef}
                 explicitModel=""
                 explicitProvider=""
                 inheritedModel={inheritedModelDefault}
@@ -924,6 +928,12 @@ export function AgentDefinitionDialog({
               />
             ) : null}
 
+            <AgentAiDefaultsDialog
+              onOpenChange={setAiDefaultsOpen}
+              open={aiDefaultsOpen}
+              returnFocusRef={aiDefaultsTriggerRef}
+            />
+
             {isCreateMode ? createRunSection : null}
 
             <div className="space-y-3">
diff --git a/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx b/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx
index 7100c59b93..1c54af73e5 100644
--- a/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx
+++ b/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx
@@ -79,6 +79,7 @@ import {
 import { getProviderApiKeyEnvVar } from "./personaDialogPickers";
 import { useAgentDialogDefaults } from "./useAgentDialogDefaults";
 import { AgentAiDefaultsNotice } from "./AgentAiDefaults";
+import { AgentAiDefaultsDialog } from "./AgentAiDefaultsDialog";
 import { useProviderApiKeyFieldState } from "./providerApiKeyFieldState";
 
 const ADVANCED_FIELDS_MOTION_TRANSITION = {
@@ -111,6 +112,8 @@ export function AgentInstanceEditDialog({
   const runtimes = runtimesQuery.data ?? [];
 
   const [name, setName] = React.useState(agent.name);
+  const [aiDefaultsOpen, setAiDefaultsOpen] = React.useState(false);
+  const aiDefaultsTriggerRef = React.useRef<HTMLButtonElement>(null);
   const [relayUrl, setRelayUrl] = React.useState(agent.relayUrl);
   const [acpCommand, setAcpCommand] = React.useState(agent.acpCommand);
   const [agentCommand, setAgentCommand] = React.useState(agent.agentCommand);
@@ -1089,13 +1092,20 @@ export function AgentInstanceEditDialog({
             </div>
 
             <AgentAiDefaultsNotice
-              confirmNavigation
+              onEditDefaults={() => setAiDefaultsOpen(true)}
+              triggerRef={aiDefaultsTriggerRef}
               explicitModel={inheritedSubmission.model ?? ""}
               explicitProvider={inheritedSubmission.provider ?? ""}
               inheritedModel={inheritedModelDefault}
               inheritedProvider={inheritedProviderDefault}
             />
 
+            <AgentAiDefaultsDialog
+              onOpenChange={setAiDefaultsOpen}
+              open={aiDefaultsOpen}
+              returnFocusRef={aiDefaultsTriggerRef}
+            />
+
             {/* Advanced settings */}
             <div className="space-y-3">
               <button
diff --git a/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx b/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx
new file mode 100644
index 0000000000..2e2fe219a6
--- /dev/null
+++ b/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx
@@ -0,0 +1,226 @@
+/**
+ * Settings card for global agent configuration defaults.
+ *
+ * Lets the user set env vars, provider, and model that apply to ALL local
+ * agents as the lowest-precedence user layer. Per-agent and persona configs
+ * always win on collision.
+ *
+ * Precedence: baked floor < GLOBAL (this card) < persona < per-agent.
+ */
+import { AlertCircle, Check, Loader } from "lucide-react";
+import * as React from "react";
+
+import { useQueryClient } from "@tanstack/react-query";
+
+import {
+  getGlobalAgentConfig,
+  setGlobalAgentConfig,
+} from "@/shared/api/tauriGlobalAgentConfig";
+import type { GlobalAgentConfig } from "@/shared/api/types";
+import { getBakedBuildEnv, type BakedEnvEntry } from "@/shared/api/tauri";
+import { globalAgentConfigQueryKey } from "@/features/agents/useGlobalAgentConfig";
+import { useAcpRuntimesQuery } from "@/features/agents/hooks";
+import {
+  GlobalAgentConfigFields,
+  EMPTY_GLOBAL_CONFIG,
+} from "@/features/agents/ui/GlobalAgentConfigFields";
+import { Button } from "@/shared/ui/button";
+
+type SaveState = "idle" | "saving" | "saved" | "error";
+
+export type GlobalAgentConfigSaveResult = Awaited<
+  ReturnType<typeof setGlobalAgentConfig>
+>;
+
+type GlobalAgentConfigEditorProps = {
+  onDirtyChange?: (dirty: boolean) => void;
+  onSaveSuccess?: (result: GlobalAgentConfigSaveResult) => void;
+  onSavingChange?: (saving: boolean) => void;
+  secondaryAction?: React.ReactNode;
+};
+
+export function GlobalAgentConfigEditor({
+  onDirtyChange,
+  onSaveSuccess,
+  onSavingChange,
+  secondaryAction,
+}: GlobalAgentConfigEditorProps) {
+  const [config, setConfig] =
+    React.useState<GlobalAgentConfig>(EMPTY_GLOBAL_CONFIG);
+  const configRef = React.useRef(config);
+  const [dirty, setDirty] = React.useState(false);
+  const [saveState, setSaveState] = React.useState<SaveState>("idle");
+  const [saveError, setSaveError] = React.useState<string | null>(null);
+  const [restartedCount, setRestartedCount] = React.useState(0);
+  const [failedRestartCount, setFailedRestartCount] = React.useState(0);
+  const [isLoading, setIsLoading] = React.useState(true);
+  const [loadError, setLoadError] = React.useState(false);
+  const [isCustomProvider, setIsCustomProvider] = React.useState(false);
+  const [isCustomModelEditing, setIsCustomModelEditing] = React.useState(false);
+  const savedTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(
+    null,
+  );
+  const queryClient = useQueryClient();
+  const [bakedEnv, setBakedEnv] = React.useState<BakedEnvEntry[]>([]);
+
+  React.useEffect(() => {
+    onDirtyChange?.(dirty);
+  }, [dirty, onDirtyChange]);
+
+  // Load on mount — seed the shared TanStack Query cache so any dialog that
+  // opens after this point reads the populated value on first render (no async
+  // race). The query is also backed by its own queryFn for first-consumer
+  // scenarios, but this eager seed eliminates the "settings card loaded, user
+  // opens Create Agent before the lazy query fires" window.
+  React.useEffect(() => {
+    let cancelled = false;
+    getGlobalAgentConfig()
+      .then((loaded) => {
+        if (!cancelled) {
+          configRef.current = loaded;
+          setConfig(loaded);
+          setIsLoading(false);
+          queryClient.setQueryData(globalAgentConfigQueryKey, loaded);
+        }
+      })
+      .catch(() => {
+        if (!cancelled) {
+          setIsLoading(false);
+          setLoadError(true);
+        }
+      });
+    return () => {
+      cancelled = true;
+    };
+  }, [queryClient]);
+
+  // Load baked build env once on mount. OSS builds return [] — the section
+  // stays hidden. Failures are silently swallowed (non-critical display data).
+  React.useEffect(() => {
+    getBakedBuildEnv()
+      .then(setBakedEnv)
+      .catch(() => {
+        // non-critical — leave bakedEnv empty
+      });
+  }, []);
+
+  // Resolve the buzz-agent runtime catalog entry for model discovery.
+  const runtimesQuery = useAcpRuntimesQuery();
+  const buzzAgentRuntime = React.useMemo(
+    () => (runtimesQuery.data ?? []).find((r) => r.id === "buzz-agent"),
+    [runtimesQuery.data],
+  );
+
+  function handleConfigChange(next: GlobalAgentConfig) {
+    configRef.current = next;
+    setConfig(next);
+    setDirty(true);
+    setSaveState("idle");
+    setSaveError(null);
+  }
+
+  async function handleSave() {
+    // Snapshot the config being submitted so we can detect edits that arrive
+    // during the IPC round-trip and avoid clobbering the user's newer input.
+    const submittedConfig = config;
+    onSavingChange?.(true);
+    setSaveState("saving");
+    setSaveError(null);
+    try {
+      const result = await setGlobalAgentConfig(submittedConfig);
+      // Apply the backend's canonical config ONLY if nothing changed during the
+      // IPC window. If the user edited, keep their newer value and leave dirty=true
+      // so they can save again. setDirty(false) runs inside the updater so both
+      // state updates batch into the same render (React 18 automatic batching).
+      const savedCurrentDraft = configRef.current === submittedConfig;
+      setConfig((current) => {
+        if (!savedCurrentDraft) {
+          // Mid-flight edit detected — do not overwrite newer user input.
+          return current;
+        }
+        configRef.current = result.config;
+        setDirty(false);
+        return result.config;
+      });
+      setRestartedCount(result.restarted_count);
+      setFailedRestartCount(result.failed_restart_count);
+      setSaveState("saved");
+      // Seed the shared TanStack Query cache with the canonical saved value so
+      // all open dialogs (and any that open afterward) see the new config
+      // synchronously — no second IPC round-trip needed.
+      queryClient.setQueryData(globalAgentConfigQueryKey, result.config);
+      if (savedCurrentDraft) onSaveSuccess?.(result);
+      if (savedTimerRef.current) clearTimeout(savedTimerRef.current);
+      // Partial restart failures require an explicit acknowledgment in the
+      // overlay, so keep their result visible instead of fading it away.
+      if (result.failed_restart_count === 0) {
+        savedTimerRef.current = setTimeout(() => setSaveState("idle"), 2500);
+      }
+    } catch (err) {
+      setSaveState("error");
+      setSaveError(typeof err === "string" ? err : "Failed to save.");
+    } finally {
+      onSavingChange?.(false);
+    }
+  }
+
+  return (
+    <div className="min-w-0 space-y-4">
+      {isLoading ? (
+        <div className="flex items-center gap-2 py-4 text-sm text-muted-foreground">
+          <Loader className="size-4 animate-spin" />
+          Loading…
+        </div>
+      ) : loadError ? (
+        <div className="flex items-center gap-2 py-4 text-sm text-destructive">
+          <AlertCircle className="size-4" />
+          Failed to load agent defaults. Restart the app to try again.
+        </div>
+      ) : (
+        <GlobalAgentConfigFields
+          bakedEnv={bakedEnv}
+          buzzAgentRuntime={buzzAgentRuntime}
+          config={config}
+          isCustomModelEditing={isCustomModelEditing}
+          isCustomProvider={isCustomProvider}
+          onConfigChange={handleConfigChange}
+          onCustomModelEditingChange={setIsCustomModelEditing}
+          onIsCustomProviderChange={setIsCustomProvider}
+        />
+      )}
+
+      {/* Save bar */}
+      {!isLoading && !loadError && (
+        <div className="mt-4 flex items-center gap-3">
+          {secondaryAction}
+          <Button
+            disabled={!dirty || saveState === "saving"}
+            onClick={() => void handleSave()}
+            size="sm"
+          >
+            {saveState === "saving" ? (
+              <Loader className="mr-1.5 size-3.5 animate-spin" />
+            ) : null}
+            Save defaults
+          </Button>
+          {saveState === "saved" && (
+            <span className="flex items-center gap-1 text-sm text-green-600 dark:text-green-400">
+              <Check className="size-3.5" />
+              {restartedCount > 0
+                ? `Saved. Restarted ${restartedCount} agent${restartedCount === 1 ? "" : "s"}.${failedRestartCount > 0 ? ` ${failedRestartCount} failed to restart — check the Agents tab.` : ""}`
+                : failedRestartCount > 0
+                  ? `Saved. ${failedRestartCount} agent${failedRestartCount === 1 ? "" : "s"} failed to restart — check the Agents tab.`
+                  : "Saved."}
+            </span>
+          )}
+          {saveState === "error" && saveError && (
+            <span className="flex items-center gap-1 text-sm text-destructive">
+              <AlertCircle className="size-3.5" />
+              {saveError}
+            </span>
+          )}
+        </div>
+      )}
+    </div>
+  );
+}
diff --git a/desktop/src/features/settings/ui/GlobalAgentConfigSettingsCard.tsx b/desktop/src/features/settings/ui/GlobalAgentConfigSettingsCard.tsx
index 5f1e82db27..56ab13a3d0 100644
--- a/desktop/src/features/settings/ui/GlobalAgentConfigSettingsCard.tsx
+++ b/desktop/src/features/settings/ui/GlobalAgentConfigSettingsCard.tsx
@@ -1,137 +1,7 @@
-/**
- * Settings card for global agent configuration defaults.
- *
- * Lets the user set env vars, provider, and model that apply to ALL local
- * agents as the lowest-precedence user layer. Per-agent and persona configs
- * always win on collision.
- *
- * Precedence: baked floor < GLOBAL (this card) < persona < per-agent.
- */
-import { AlertCircle, Check, Loader } from "lucide-react";
-import * as React from "react";
-
-import { useQueryClient } from "@tanstack/react-query";
-
-import {
-  getGlobalAgentConfig,
-  setGlobalAgentConfig,
-} from "@/shared/api/tauriGlobalAgentConfig";
-import type { GlobalAgentConfig } from "@/shared/api/types";
-import { getBakedBuildEnv, type BakedEnvEntry } from "@/shared/api/tauri";
-import { globalAgentConfigQueryKey } from "@/features/agents/useGlobalAgentConfig";
-import { useAcpRuntimesQuery } from "@/features/agents/hooks";
-import {
-  GlobalAgentConfigFields,
-  EMPTY_GLOBAL_CONFIG,
-} from "@/features/agents/ui/GlobalAgentConfigFields";
-import { Button } from "@/shared/ui/button";
+import { GlobalAgentConfigEditor } from "@/features/agents/ui/GlobalAgentConfigEditor";
 import { SectionHeader } from "@/shared/ui/PageHeader";
 
-type SaveState = "idle" | "saving" | "saved" | "error";
-
 export function GlobalAgentConfigSettingsCard() {
-  const [config, setConfig] =
-    React.useState<GlobalAgentConfig>(EMPTY_GLOBAL_CONFIG);
-  const [dirty, setDirty] = React.useState(false);
-  const [saveState, setSaveState] = React.useState<SaveState>("idle");
-  const [saveError, setSaveError] = React.useState<string | null>(null);
-  const [restartedCount, setRestartedCount] = React.useState(0);
-  const [failedRestartCount, setFailedRestartCount] = React.useState(0);
-  const [isLoading, setIsLoading] = React.useState(true);
-  const [loadError, setLoadError] = React.useState(false);
-  const [isCustomProvider, setIsCustomProvider] = React.useState(false);
-  const [isCustomModelEditing, setIsCustomModelEditing] = React.useState(false);
-  const savedTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(
-    null,
-  );
-  const queryClient = useQueryClient();
-  const [bakedEnv, setBakedEnv] = React.useState<BakedEnvEntry[]>([]);
-
-  // Load on mount — seed the shared TanStack Query cache so any dialog that
-  // opens after this point reads the populated value on first render (no async
-  // race). The query is also backed by its own queryFn for first-consumer
-  // scenarios, but this eager seed eliminates the "settings card loaded, user
-  // opens Create Agent before the lazy query fires" window.
-  React.useEffect(() => {
-    let cancelled = false;
-    getGlobalAgentConfig()
-      .then((loaded) => {
-        if (!cancelled) {
-          setConfig(loaded);
-          setIsLoading(false);
-          queryClient.setQueryData(globalAgentConfigQueryKey, loaded);
-        }
-      })
-      .catch(() => {
-        if (!cancelled) {
-          setIsLoading(false);
-          setLoadError(true);
-        }
-      });
-    return () => {
-      cancelled = true;
-    };
-  }, [queryClient]);
-
-  // Load baked build env once on mount. OSS builds return [] — the section
-  // stays hidden. Failures are silently swallowed (non-critical display data).
-  React.useEffect(() => {
-    getBakedBuildEnv()
-      .then(setBakedEnv)
-      .catch(() => {
-        // non-critical — leave bakedEnv empty
-      });
-  }, []);
-
-  // Resolve the buzz-agent runtime catalog entry for model discovery.
-  const runtimesQuery = useAcpRuntimesQuery();
-  const buzzAgentRuntime = React.useMemo(
-    () => (runtimesQuery.data ?? []).find((r) => r.id === "buzz-agent"),
-    [runtimesQuery.data],
-  );
-
-  function handleConfigChange(next: GlobalAgentConfig) {
-    setConfig(next);
-    setDirty(true);
-    setSaveState("idle");
-    setSaveError(null);
-  }
-
-  async function handleSave() {
-    // Snapshot the config being submitted so we can detect edits that arrive
-    // during the IPC round-trip and avoid clobbering the user's newer input.
-    const submittedConfig = config;
-    setSaveState("saving");
-    setSaveError(null);
-    try {
-      const result = await setGlobalAgentConfig(submittedConfig);
-      // Apply the backend's canonical config ONLY if nothing changed during the
-      // IPC window. If the user edited, keep their newer value and leave dirty=true
-      // so they can save again. setDirty(false) runs inside the updater so both
-      // state updates batch into the same render (React 18 automatic batching).
-      setConfig((current) => {
-        if (current !== submittedConfig) {
-          // Mid-flight edit detected — do not overwrite newer user input.
-          return current;
-        }
-        setDirty(false);
-        return result.config;
-      });
-      setRestartedCount(result.restarted_count);
-      setFailedRestartCount(result.failed_restart_count);
-      setSaveState("saved");
-      // Seed the shared TanStack Query cache with the canonical saved value so
-      // all open dialogs (and any that open afterward) see the new config
-      // synchronously — no second IPC round-trip needed.
-      queryClient.setQueryData(globalAgentConfigQueryKey, result.config);
-      if (savedTimerRef.current) clearTimeout(savedTimerRef.current);
-      savedTimerRef.current = setTimeout(() => setSaveState("idle"), 2500);
-    } catch (err) {
-      setSaveState("error");
-      setSaveError(typeof err === "string" ? err : "Failed to save.");
-    }
-  }
-
   return (
     <section
       className="min-w-0 space-y-4"
@@ -141,61 +11,7 @@ export function GlobalAgentConfigSettingsCard() {
         title="AI defaults"
         description="Provider, model, effort, and environment settings inherited by local agents. Agent-specific settings always take priority."
       />
-
-      {isLoading ? (
-        <div className="flex items-center gap-2 py-4 text-sm text-muted-foreground">
-          <Loader className="size-4 animate-spin" />
-          Loading…
-        </div>
-      ) : loadError ? (
-        <div className="flex items-center gap-2 py-4 text-sm text-destructive">
-          <AlertCircle className="size-4" />
-          Failed to load agent defaults. Restart the app to try again.
-        </div>
-      ) : (
-        <GlobalAgentConfigFields
-          bakedEnv={bakedEnv}
-          buzzAgentRuntime={buzzAgentRuntime}
-          config={config}
-          isCustomModelEditing={isCustomModelEditing}
-          isCustomProvider={isCustomProvider}
-          onConfigChange={handleConfigChange}
-          onCustomModelEditingChange={setIsCustomModelEditing}
-          onIsCustomProviderChange={setIsCustomProvider}
-        />
-      )}
-
-      {/* Save bar */}
-      {!isLoading && !loadError && (
-        <div className="mt-4 flex items-center gap-3">
-          <Button
-            disabled={!dirty || saveState === "saving"}
-            onClick={() => void handleSave()}
-            size="sm"
-          >
-            {saveState === "saving" ? (
-              <Loader className="mr-1.5 size-3.5 animate-spin" />
-            ) : null}
-            Save defaults
-          </Button>
-          {saveState === "saved" && (
-            <span className="flex items-center gap-1 text-sm text-green-600 dark:text-green-400">
-              <Check className="size-3.5" />
-              {restartedCount > 0
-                ? `Saved. Restarted ${restartedCount} agent${restartedCount === 1 ? "" : "s"}.${failedRestartCount > 0 ? ` ${failedRestartCount} failed to restart — check the Agents tab.` : ""}`
-                : failedRestartCount > 0
-                  ? `Saved. ${failedRestartCount} agent${failedRestartCount === 1 ? "" : "s"} failed to restart — check the Agents tab.`
-                  : "Saved."}
-            </span>
-          )}
-          {saveState === "error" && saveError && (
-            <span className="flex items-center gap-1 text-sm text-destructive">
-              <AlertCircle className="size-3.5" />
-              {saveError}
-            </span>
-          )}
-        </div>
-      )}
+      <GlobalAgentConfigEditor />
     </section>
   );
 }

From a71e31122d5468d4718c6ad9e9e786e1d90777e8 Mon Sep 17 00:00:00 2001
From: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex
 <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 17:34:46 -0700
Subject: [PATCH 03/17] Restore focus after keeping AI defaults edits

Co-authored-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
---
 .../agents/ui/AgentAiDefaultsDialog.tsx       | 21 +++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/desktop/src/features/agents/ui/AgentAiDefaultsDialog.tsx b/desktop/src/features/agents/ui/AgentAiDefaultsDialog.tsx
index 25b82cc47e..22a8ba766e 100644
--- a/desktop/src/features/agents/ui/AgentAiDefaultsDialog.tsx
+++ b/desktop/src/features/agents/ui/AgentAiDefaultsDialog.tsx
@@ -36,6 +36,8 @@ export function AgentAiDefaultsDialog({
   const [saving, setSaving] = React.useState(false);
   const [confirmDiscard, setConfirmDiscard] = React.useState(false);
   const [restartFailures, setRestartFailures] = React.useState(0);
+  const secondaryActionRef = React.useRef<HTMLButtonElement>(null);
+  const restoreDefaultsFocusRef = React.useRef(false);
 
   const closeAndRestoreFocus = React.useCallback(() => {
     onOpenChange(false);
@@ -97,6 +99,7 @@ export function AgentAiDefaultsDialog({
             secondaryAction={
               <Button
                 disabled={saving}
+                ref={secondaryActionRef}
                 onClick={
                   restartFailures > 0 ? closeAndRestoreFocus : requestClose
                 }
@@ -112,7 +115,15 @@ export function AgentAiDefaultsDialog({
       </Dialog>
 
       <AlertDialog open={confirmDiscard} onOpenChange={setConfirmDiscard}>
-        <AlertDialogContent data-testid="discard-ai-defaults-dialog">
+        <AlertDialogContent
+          data-testid="discard-ai-defaults-dialog"
+          onCloseAutoFocus={(event) => {
+            if (!restoreDefaultsFocusRef.current) return;
+            event.preventDefault();
+            restoreDefaultsFocusRef.current = false;
+            requestAnimationFrame(() => secondaryActionRef.current?.focus());
+          }}
+        >
           <AlertDialogHeader>
             <AlertDialogTitle>Discard changes to AI defaults?</AlertDialogTitle>
             <AlertDialogDescription>
@@ -121,7 +132,13 @@ export function AgentAiDefaultsDialog({
             </AlertDialogDescription>
           </AlertDialogHeader>
           <AlertDialogFooter>
-            <AlertDialogCancel>Keep editing</AlertDialogCancel>
+            <AlertDialogCancel
+              onClick={() => {
+                restoreDefaultsFocusRef.current = true;
+              }}
+            >
+              Keep editing
+            </AlertDialogCancel>
             <AlertDialogAction asChild>
               <Button
                 onClick={() => {

From 87da603f200e18ff51d9061ad23bf375a461c49f Mon Sep 17 00:00:00 2001
From: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex
 <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 17:52:36 -0700
Subject: [PATCH 04/17] Require a resolvable global default model

Co-authored-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
---
 .../agents/ui/GlobalAgentConfigEditor.tsx     |  4 +-
 .../agents/ui/GlobalAgentConfigFields.tsx     | 58 +++++++++++++++++--
 .../agents/ui/personaProviderModelFields.tsx  | 29 ++++++----
 3 files changed, 75 insertions(+), 16 deletions(-)

diff --git a/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx b/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx
index 2e2fe219a6..23382336e8 100644
--- a/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx
+++ b/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx
@@ -55,6 +55,7 @@ export function GlobalAgentConfigEditor({
   const [failedRestartCount, setFailedRestartCount] = React.useState(0);
   const [isLoading, setIsLoading] = React.useState(true);
   const [loadError, setLoadError] = React.useState(false);
+  const [configIsValid, setConfigIsValid] = React.useState(true);
   const [isCustomProvider, setIsCustomProvider] = React.useState(false);
   const [isCustomModelEditing, setIsCustomModelEditing] = React.useState(false);
   const savedTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(
@@ -186,6 +187,7 @@ export function GlobalAgentConfigEditor({
           onConfigChange={handleConfigChange}
           onCustomModelEditingChange={setIsCustomModelEditing}
           onIsCustomProviderChange={setIsCustomProvider}
+          onValidityChange={setConfigIsValid}
         />
       )}
 
@@ -194,7 +196,7 @@ export function GlobalAgentConfigEditor({
         <div className="mt-4 flex items-center gap-3">
           {secondaryAction}
           <Button
-            disabled={!dirty || saveState === "saving"}
+            disabled={!dirty || !configIsValid || saveState === "saving"}
             onClick={() => void handleSave()}
             size="sm"
           >
diff --git a/desktop/src/features/agents/ui/GlobalAgentConfigFields.tsx b/desktop/src/features/agents/ui/GlobalAgentConfigFields.tsx
index 80e24955df..3b2c84f293 100644
--- a/desktop/src/features/agents/ui/GlobalAgentConfigFields.tsx
+++ b/desktop/src/features/agents/ui/GlobalAgentConfigFields.tsx
@@ -48,6 +48,42 @@ const BAKED_STRUCTURED_KEYS = new Set([
   BUZZ_AGENT_THINKING_EFFORT,
 ]);
 
+function providerModelEnvKey(provider: string): string | null {
+  switch (provider.trim().toLowerCase()) {
+    case "databricks":
+    case "databricks_v2":
+    case "databricks-v2":
+      return "DATABRICKS_MODEL";
+    case "anthropic":
+      return "ANTHROPIC_MODEL";
+    case "openai":
+    case "openai-compat":
+      return "OPENAI_COMPAT_MODEL";
+    default:
+      return null;
+  }
+}
+
+export function getGlobalModelFallback(
+  bakedEnv: readonly BakedEnvEntry[],
+  provider: string,
+  globalEnv: Readonly<Record<string, string>> = {},
+): string | null {
+  const universal = bakedEnv.find(
+    (entry) => entry.key === "BUZZ_AGENT_MODEL" && !entry.masked,
+  )?.value;
+  if (universal?.trim()) return universal.trim();
+
+  const providerKey = providerModelEnvKey(provider);
+  if (!providerKey) return provider === "relay-mesh" ? "auto" : null;
+  const globalProviderModel = globalEnv[providerKey]?.trim();
+  if (globalProviderModel) return globalProviderModel;
+  const providerModel = bakedEnv.find(
+    (entry) => entry.key === providerKey && !entry.masked,
+  )?.value;
+  return providerModel?.trim() || null;
+}
+
 export type GlobalAgentConfigFieldsProps = {
   bakedEnv: BakedEnvEntry[];
   buzzAgentRuntime: AcpRuntimeCatalogEntry | undefined;
@@ -57,6 +93,7 @@ export type GlobalAgentConfigFieldsProps = {
   onConfigChange: (next: GlobalAgentConfig) => void;
   onCustomModelEditingChange: (value: boolean) => void;
   onIsCustomProviderChange: (value: boolean) => void;
+  onValidityChange?: (valid: boolean) => void;
 };
 
 export function GlobalAgentConfigFields({
@@ -68,15 +105,22 @@ export function GlobalAgentConfigFields({
   onConfigChange,
   onCustomModelEditingChange,
   onIsCustomProviderChange,
+  onValidityChange,
 }: GlobalAgentConfigFieldsProps) {
   const bakedProvider = React.useMemo(
     () => bakedEnv.find((e) => e.key === "BUZZ_AGENT_PROVIDER")?.value ?? null,
     [bakedEnv],
   );
-  const bakedModel = React.useMemo(
-    () => bakedEnv.find((e) => e.key === "BUZZ_AGENT_MODEL")?.value ?? null,
-    [bakedEnv],
+  const effectiveProvider = config.provider?.trim() || bakedProvider || "";
+  const fallbackModel = React.useMemo(
+    () => getGlobalModelFallback(bakedEnv, effectiveProvider, config.env_vars),
+    [bakedEnv, config.env_vars, effectiveProvider],
   );
+  const modelIsValid =
+    (config.model?.trim().length ?? 0) > 0 || fallbackModel !== null;
+  React.useEffect(() => {
+    onValidityChange?.(modelIsValid);
+  }, [modelIsValid, onValidityChange]);
   const bakedEffort = React.useMemo(
     () =>
       bakedEnv.find((e) => e.key === BUZZ_AGENT_THINKING_EFFORT)?.value ?? null,
@@ -229,12 +273,16 @@ export function GlobalAgentConfigFields({
       {/* Model field */}
       <div className="space-y-1.5 p-3">
         <AgentModelField
+          allowDefaultModel={fallbackModel !== null}
+          defaultModelLabel={
+            fallbackModel ? `Default model (${fallbackModel})` : undefined
+          }
           disabled={false}
           discoveredModelOptions={discoveredModelOptions}
-          globalModel={bakedModel ?? undefined}
+          globalModel={fallbackModel ?? undefined}
           id="global-agent-model"
           isCustomModelEditing={isCustomModelEditing}
-          isRequired={false}
+          isRequired={fallbackModel === null}
           model={config.model ?? ""}
           modelDiscoveryLoading={modelDiscoveryLoading}
           modelDiscoveryStatus={modelDiscoveryStatus}
diff --git a/desktop/src/features/agents/ui/personaProviderModelFields.tsx b/desktop/src/features/agents/ui/personaProviderModelFields.tsx
index b5f0049e4a..7347aa5178 100644
--- a/desktop/src/features/agents/ui/personaProviderModelFields.tsx
+++ b/desktop/src/features/agents/ui/personaProviderModelFields.tsx
@@ -48,6 +48,8 @@ export function AgentModelField({
   disabled,
   discoveredModelOptions,
   globalModel,
+  allowDefaultModel = true,
+  defaultModelLabel,
   id = "agent-model",
   isCustomModelEditing,
   isRequired,
@@ -60,8 +62,12 @@ export function AgentModelField({
 }: {
   disabled: boolean;
   discoveredModelOptions: readonly PersonaModelOption[] | null;
-  /** Global model default; when set, the zero-value option reads `Inherit global default (<model>)`. */
+  /** Known lower-precedence model used when this field is left empty. */
   globalModel?: string;
+  /** Hide the empty/default option when no valid lower-precedence model exists. */
+  allowDefaultModel?: boolean;
+  /** Source-aware label for the empty/default option. */
+  defaultModelLabel?: string;
   /** DOM id for the model select. Defaults to `"agent-model"`. Override in
    *  contexts where multiple instances coexist on the same page (e.g. the
    *  global-config settings card) to avoid duplicate DOM ids. */
@@ -80,20 +86,23 @@ export function AgentModelField({
 
   // Buzz shared compute always has an automatic routing choice, even when
   // discovery is empty or returns only explicit live model ids.
-  const staticModelOptions: readonly PersonaModelOption[] = [
-    {
-      id: "",
-      label: isSharedCompute
+  const defaultOption: PersonaModelOption = {
+    id: "",
+    label:
+      defaultModelLabel ??
+      (isSharedCompute
         ? "Default (auto)"
-        : getDefaultLlmModelLabel(globalModel),
-    },
-  ];
+        : getDefaultLlmModelLabel(globalModel)),
+  };
   const discoveredWithoutDefault = (discoveredModelOptions ?? []).filter(
     (option) => option.id.trim() !== "",
   );
   const effectiveModelOptions = isSharedCompute
-    ? [...staticModelOptions, ...discoveredWithoutDefault]
-    : (discoveredModelOptions ?? staticModelOptions);
+    ? [defaultOption, ...discoveredWithoutDefault]
+    : [
+        ...(allowDefaultModel ? [defaultOption] : []),
+        ...discoveredWithoutDefault,
+      ];
 
   // isModelCustom: true when the current model isn't in any known option set.
   // We check discovered options (when available) or runtime-static options so

From c3cd62517c47ea66334d2a88fa0b6a6c7841dcc2 Mon Sep 17 00:00:00 2001
From: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex
 <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 18:02:25 -0700
Subject: [PATCH 05/17] Share resolved model fallback with agent dialogs

Co-authored-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
---
 .../agents/ui/GlobalAgentConfigFields.tsx     | 41 ++----------
 .../src/features/agents/ui/bakedEnvHelpers.ts | 63 +++++++++++++++++--
 .../ui/createAgentLocalModeGate.test.mjs      | 52 +++++++++++++++
 3 files changed, 114 insertions(+), 42 deletions(-)

diff --git a/desktop/src/features/agents/ui/GlobalAgentConfigFields.tsx b/desktop/src/features/agents/ui/GlobalAgentConfigFields.tsx
index 3b2c84f293..a5d2335ac7 100644
--- a/desktop/src/features/agents/ui/GlobalAgentConfigFields.tsx
+++ b/desktop/src/features/agents/ui/GlobalAgentConfigFields.tsx
@@ -14,7 +14,10 @@ import type {
 } from "@/shared/api/types";
 import { EnvVarsEditor } from "@/features/agents/ui/EnvVarsEditor";
 import type { InheritedEnvRow } from "@/features/agents/ui/EnvVarsEditor";
-import { getBakedProviderInheritLabel } from "@/features/agents/ui/bakedEnvHelpers";
+import {
+  getBakedProviderInheritLabel,
+  getGlobalModelFallback,
+} from "@/features/agents/ui/bakedEnvHelpers";
 import {
   AUTO_PROVIDER_DROPDOWN_VALUE,
   BLOCK_BUILD_HIDDEN_PROVIDER_IDS,
@@ -48,42 +51,6 @@ const BAKED_STRUCTURED_KEYS = new Set([
   BUZZ_AGENT_THINKING_EFFORT,
 ]);
 
-function providerModelEnvKey(provider: string): string | null {
-  switch (provider.trim().toLowerCase()) {
-    case "databricks":
-    case "databricks_v2":
-    case "databricks-v2":
-      return "DATABRICKS_MODEL";
-    case "anthropic":
-      return "ANTHROPIC_MODEL";
-    case "openai":
-    case "openai-compat":
-      return "OPENAI_COMPAT_MODEL";
-    default:
-      return null;
-  }
-}
-
-export function getGlobalModelFallback(
-  bakedEnv: readonly BakedEnvEntry[],
-  provider: string,
-  globalEnv: Readonly<Record<string, string>> = {},
-): string | null {
-  const universal = bakedEnv.find(
-    (entry) => entry.key === "BUZZ_AGENT_MODEL" && !entry.masked,
-  )?.value;
-  if (universal?.trim()) return universal.trim();
-
-  const providerKey = providerModelEnvKey(provider);
-  if (!providerKey) return provider === "relay-mesh" ? "auto" : null;
-  const globalProviderModel = globalEnv[providerKey]?.trim();
-  if (globalProviderModel) return globalProviderModel;
-  const providerModel = bakedEnv.find(
-    (entry) => entry.key === providerKey && !entry.masked,
-  )?.value;
-  return providerModel?.trim() || null;
-}
-
 export type GlobalAgentConfigFieldsProps = {
   bakedEnv: BakedEnvEntry[];
   buzzAgentRuntime: AcpRuntimeCatalogEntry | undefined;
diff --git a/desktop/src/features/agents/ui/bakedEnvHelpers.ts b/desktop/src/features/agents/ui/bakedEnvHelpers.ts
index 1463c2aed8..5f3808ad9b 100644
--- a/desktop/src/features/agents/ui/bakedEnvHelpers.ts
+++ b/desktop/src/features/agents/ui/bakedEnvHelpers.ts
@@ -58,6 +58,43 @@ export function getBakedModelInheritLabel(bakedModelId: string): string {
   return `Inherit build default (${bakedModelId})`;
 }
 
+function providerModelEnvKey(provider: string): string | null {
+  switch (provider.trim().toLowerCase()) {
+    case "databricks":
+    case "databricks_v2":
+    case "databricks-v2":
+      return "DATABRICKS_MODEL";
+    case "anthropic":
+      return "ANTHROPIC_MODEL";
+    case "openai":
+    case "openai-compat":
+      return "OPENAI_COMPAT_MODEL";
+    default:
+      return null;
+  }
+}
+
+/** Resolve the concrete model beneath the structured global model override. */
+export function getGlobalModelFallback(
+  bakedEnv: readonly BakedEnvEntry[] | undefined,
+  provider: string,
+  globalEnv: Readonly<Record<string, string>> = {},
+): string | null {
+  const universal = bakedEnv?.find(
+    (entry) => entry.key === "BUZZ_AGENT_MODEL" && !entry.masked,
+  )?.value;
+  if (universal?.trim()) return universal.trim();
+
+  const providerKey = providerModelEnvKey(provider);
+  if (!providerKey) return provider === "relay-mesh" ? "auto" : null;
+  const globalProviderModel = globalEnv[providerKey]?.trim();
+  if (globalProviderModel) return globalProviderModel;
+  const providerModel = bakedEnv?.find(
+    (entry) => entry.key === providerKey && !entry.masked,
+  )?.value;
+  return providerModel?.trim() || null;
+}
+
 /**
  * Global env keys are arbitrary user input. Count only ordinary configuration
  * names in the collapsed summary so it never calls attention to credentials.
@@ -117,11 +154,27 @@ export function getInheritedAgentDefaults(
       bakedEnv,
       "BUZZ_AGENT_PROVIDER",
     ),
-    model: resolveInheritedDefault(
-      globalConfig.model,
-      bakedEnv,
-      "BUZZ_AGENT_MODEL",
-    ),
+    model: (() => {
+      const structured = resolveInheritedDefault(
+        globalConfig.model,
+        bakedEnv,
+        "BUZZ_AGENT_MODEL",
+      );
+      if (structured.value) return structured;
+      const provider = resolveInheritedDefault(
+        globalConfig.provider,
+        bakedEnv,
+        "BUZZ_AGENT_PROVIDER",
+      );
+      const fallback = getGlobalModelFallback(
+        bakedEnv,
+        provider.value,
+        globalConfig.env_vars,
+      );
+      return fallback
+        ? { source: "build", value: fallback }
+        : { source: null, value: "" };
+    })(),
     effort: resolveInheritedDefault(
       globalConfig.env_vars.BUZZ_AGENT_THINKING_EFFORT,
       bakedEnv,
diff --git a/desktop/src/features/agents/ui/createAgentLocalModeGate.test.mjs b/desktop/src/features/agents/ui/createAgentLocalModeGate.test.mjs
index a4f536652e..2b5269b8ba 100644
--- a/desktop/src/features/agents/ui/createAgentLocalModeGate.test.mjs
+++ b/desktop/src/features/agents/ui/createAgentLocalModeGate.test.mjs
@@ -36,6 +36,8 @@ import {
   countNonSecretInheritedEnvVars,
   getBakedModelInheritLabel,
   getAdvancedInheritedSummary,
+  getGlobalModelFallback,
+  getInheritedAgentDefaults,
   getBakedProviderInheritLabel,
   resolveInheritedDefault,
 } from "./bakedEnvHelpers.ts";
@@ -1390,3 +1392,53 @@ test("localMode_globalEnvSatisfied_agentLocalKeyAbsent_silenced", () => {
     "gate must be satisfied when global key is not shadowed by an explicit empty",
   );
 });
+
+test("global model fallback resolves the selected provider model env", () => {
+  const bakedEnv = [
+    {
+      key: "DATABRICKS_MODEL",
+      value: "goose-claude-opus-4-8",
+      masked: false,
+    },
+  ];
+  assert.equal(
+    getGlobalModelFallback(bakedEnv, "databricks_v2"),
+    "goose-claude-opus-4-8",
+  );
+});
+
+test("global model fallback gives saved provider env precedence over build env", () => {
+  const bakedEnv = [
+    { key: "ANTHROPIC_MODEL", value: "build-model", masked: false },
+  ];
+  assert.equal(
+    getGlobalModelFallback(bakedEnv, "anthropic", {
+      ANTHROPIC_MODEL: "saved-model",
+    }),
+    "saved-model",
+  );
+});
+
+test("global model fallback never uses another provider's model", () => {
+  const bakedEnv = [
+    { key: "DATABRICKS_MODEL", value: "databricks-model", masked: false },
+  ];
+  assert.equal(getGlobalModelFallback(bakedEnv, "anthropic"), null);
+});
+
+test("inherited defaults expose a provider-specific model fallback to agent dialogs", () => {
+  const defaults = getInheritedAgentDefaults(
+    { env_vars: {}, provider: "databricks_v2", model: null },
+    [
+      {
+        key: "DATABRICKS_MODEL",
+        value: "goose-claude-opus-4-8",
+        masked: false,
+      },
+    ],
+  );
+  assert.deepEqual(defaults.model, {
+    source: "build",
+    value: "goose-claude-opus-4-8",
+  });
+});

From cd74bc1013b272fffb28e95bb9fa86655ee2d330 Mon Sep 17 00:00:00 2001
From: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf
 <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 18:14:40 -0700
Subject: [PATCH 06/17] Simplify inherited AI defaults summary

Co-authored-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
---
 .../agents/ui/AgentAiConfigurationMode.tsx    | 14 ++---
 .../features/agents/ui/AgentAiDefaults.tsx    | 58 +++++--------------
 .../agents/ui/AgentDefinitionDialog.tsx       | 11 +---
 3 files changed, 22 insertions(+), 61 deletions(-)

diff --git a/desktop/src/features/agents/ui/AgentAiConfigurationMode.tsx b/desktop/src/features/agents/ui/AgentAiConfigurationMode.tsx
index 33b7ddd12f..fc29078c55 100644
--- a/desktop/src/features/agents/ui/AgentAiConfigurationMode.tsx
+++ b/desktop/src/features/agents/ui/AgentAiConfigurationMode.tsx
@@ -4,11 +4,9 @@ import type { AgentAiConfigurationMode } from "./agentAiConfigurationPolicy";
 export type { AgentAiConfigurationMode } from "./agentAiConfigurationPolicy";
 
 export function AgentAiConfigurationModeField({
-  defaultsSummary,
   mode,
   onModeChange,
 }: {
-  defaultsSummary: string;
   mode: AgentAiConfigurationMode;
   onModeChange: (mode: AgentAiConfigurationMode) => void;
 }) {
@@ -26,13 +24,11 @@ export function AgentAiConfigurationModeField({
           <TabsTrigger value="custom">Customize for this agent</TabsTrigger>
         </TabsList>
       </Tabs>
-      <p className="text-xs text-muted-foreground">
-        {mode === "defaults"
-          ? defaultsSummary === "Not configured"
-            ? "AI defaults aren’t configured yet."
-            : `${defaultsSummary}. Future default changes apply automatically.`
-          : "Provider and model changes apply only to this agent."}
-      </p>
+      {mode === "custom" ? (
+        <p className="text-xs text-muted-foreground">
+          Provider and model changes apply only to this agent.
+        </p>
+      ) : null}
     </div>
   );
 }
diff --git a/desktop/src/features/agents/ui/AgentAiDefaults.tsx b/desktop/src/features/agents/ui/AgentAiDefaults.tsx
index bf37de5044..aec10c0866 100644
--- a/desktop/src/features/agents/ui/AgentAiDefaults.tsx
+++ b/desktop/src/features/agents/ui/AgentAiDefaults.tsx
@@ -40,57 +40,31 @@ export function AgentAiDefaultsNotice({
   inheritedModel: InheritedDefault;
   inheritedProvider: InheritedDefault;
 }) {
-  const inheritsProvider = explicitProvider.trim().length === 0;
-  const inheritsModel = explicitModel.trim().length === 0;
-
-  const usesCustomConfig = !inheritsProvider && !inheritsModel;
-  const requiredProviderMissing = inheritsProvider && !inheritedProvider.value;
-
-  const inheritedParts = [
-    inheritsProvider
-      ? inheritedProvider.value
-        ? `Provider ${providerLabel(inheritedProvider.value)}`
-        : "Provider not configured"
-      : null,
-    inheritsModel
-      ? inheritedModel.value
-        ? `Model ${inheritedModel.value}`
-        : "Model not configured"
-      : null,
-  ].filter((value): value is string => Boolean(value));
+  const provider = explicitProvider.trim() || inheritedProvider.value;
+  const model = explicitModel.trim() || inheritedModel.value;
 
   return (
-    <div
-      className="flex flex-wrap items-center justify-between gap-2 rounded-lg border border-border/60 bg-muted/30 px-3 py-2"
-      data-testid="agent-ai-defaults-notice"
-    >
-      <div className="min-w-0">
-        <p className="text-xs font-medium text-foreground">
-          {usesCustomConfig
-            ? "Custom AI configuration"
-            : requiredProviderMissing
-              ? "AI defaults aren’t configured"
-              : inheritsProvider && inheritsModel
-                ? "Uses AI defaults"
-                : "Partially uses AI defaults"}
-        </p>
-        <p className="text-xs text-muted-foreground">
-          {usesCustomConfig
-            ? "This agent won’t follow provider or model default changes."
-            : requiredProviderMissing
-              ? "Choose a provider in AI defaults to use this agent."
-              : `${inheritedParts.join(" · ")}. Inherited fields follow future changes.`}
-        </p>
-      </div>
+    <div className="space-y-1" data-testid="agent-ai-defaults-notice">
+      <dl className="grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 text-sm">
+        <dt className="text-muted-foreground">Provider</dt>
+        <dd className="truncate text-foreground">
+          {provider ? providerLabel(provider) : "Not configured"}
+        </dd>
+        <dt className="text-muted-foreground">Model</dt>
+        <dd className="truncate text-foreground">
+          {model || "Not configured"}
+        </dd>
+      </dl>
       <Button
+        className="h-auto px-0 py-1"
+        data-testid="edit-ai-defaults"
         onClick={onEditDefaults}
         ref={triggerRef}
-        data-testid="edit-ai-defaults"
         size="xs"
         type="button"
         variant="link"
       >
-        Edit AI defaults
+        Edit global defaults
       </Button>
     </div>
   );
diff --git a/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx b/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx
index 7c2d838414..a59116895f 100644
--- a/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx
+++ b/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx
@@ -68,10 +68,7 @@ import {
 } from "./usePersonaModelDiscovery";
 import { useBakedBuildEnvKeysQuery, useRuntimeFileConfigQuery } from "../hooks";
 import { useAgentDialogDefaults } from "./useAgentDialogDefaults";
-import {
-  AgentAiDefaultsNotice,
-  formatAiDefaultsSummary,
-} from "./AgentAiDefaults";
+import { AgentAiDefaultsNotice } from "./AgentAiDefaults";
 import { AgentAiDefaultsDialog } from "./AgentAiDefaultsDialog";
 import {
   AgentAiConfigurationModeField,
@@ -343,11 +340,6 @@ export function AgentDefinitionDialog({
     },
     inheritedEnvVars: inheritedEnvVarsForAdvanced,
   } = useAgentDialogDefaults({ open });
-  const aiDefaultsSummary = formatAiDefaultsSummary({
-    provider: inheritedProviderDefault,
-    model: inheritedModelDefault,
-  });
-
   function handleAiConfigurationModeChange(nextMode: AgentAiConfigurationMode) {
     setAiConfigurationMode(nextMode);
     setIsCustomProviderEditing(false);
@@ -821,7 +813,6 @@ export function AgentDefinitionDialog({
 
             {llmProviderFieldVisible ? (
               <AgentAiConfigurationModeField
-                defaultsSummary={aiDefaultsSummary}
                 mode={aiConfigurationMode}
                 onModeChange={handleAiConfigurationModeChange}
               />

From fc98b1b08d9ad6280efef8f1c6198e00ab03d936 Mon Sep 17 00:00:00 2001
From: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf
 <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 18:18:05 -0700
Subject: [PATCH 07/17] Align AI defaults actions to the right

Co-authored-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
---
 .../agents/ui/GlobalAgentConfigEditor.tsx     | 34 ++++++++++---------
 1 file changed, 18 insertions(+), 16 deletions(-)

diff --git a/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx b/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx
index 23382336e8..96f760eb85 100644
--- a/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx
+++ b/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx
@@ -193,21 +193,10 @@ export function GlobalAgentConfigEditor({
 
       {/* Save bar */}
       {!isLoading && !loadError && (
-        <div className="mt-4 flex items-center gap-3">
-          {secondaryAction}
-          <Button
-            disabled={!dirty || !configIsValid || saveState === "saving"}
-            onClick={() => void handleSave()}
-            size="sm"
-          >
-            {saveState === "saving" ? (
-              <Loader className="mr-1.5 size-3.5 animate-spin" />
-            ) : null}
-            Save defaults
-          </Button>
+        <div className="mt-4 flex flex-wrap items-center gap-3">
           {saveState === "saved" && (
-            <span className="flex items-center gap-1 text-sm text-green-600 dark:text-green-400">
-              <Check className="size-3.5" />
+            <span className="flex min-w-0 items-center gap-1 text-sm text-green-600 dark:text-green-400">
+              <Check className="size-3.5 shrink-0" />
               {restartedCount > 0
                 ? `Saved. Restarted ${restartedCount} agent${restartedCount === 1 ? "" : "s"}.${failedRestartCount > 0 ? ` ${failedRestartCount} failed to restart — check the Agents tab.` : ""}`
                 : failedRestartCount > 0
@@ -216,11 +205,24 @@ export function GlobalAgentConfigEditor({
             </span>
           )}
           {saveState === "error" && saveError && (
-            <span className="flex items-center gap-1 text-sm text-destructive">
-              <AlertCircle className="size-3.5" />
+            <span className="flex min-w-0 items-center gap-1 text-sm text-destructive">
+              <AlertCircle className="size-3.5 shrink-0" />
               {saveError}
             </span>
           )}
+          <div className="ml-auto flex items-center gap-3">
+            {secondaryAction}
+            <Button
+              disabled={!dirty || !configIsValid || saveState === "saving"}
+              onClick={() => void handleSave()}
+              size="sm"
+            >
+              {saveState === "saving" ? (
+                <Loader className="mr-1.5 size-3.5 animate-spin" />
+              ) : null}
+              Save defaults
+            </Button>
+          </div>
         </div>
       )}
     </div>

From dde1a7e8698ec24af021a8e9d7d84ae3c4f27f7b Mon Sep 17 00:00:00 2001
From: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf
 <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 18:21:42 -0700
Subject: [PATCH 08/17] Remove collapsed advanced defaults summary

Co-authored-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
---
 .../features/agents/ui/AgentDefinitionDialog.tsx  |  7 -------
 .../agents/ui/AgentInstanceEditDialog.tsx         |  7 -------
 .../features/agents/ui/useAgentDialogDefaults.ts  | 15 +--------------
 3 files changed, 1 insertion(+), 28 deletions(-)

diff --git a/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx b/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx
index a59116895f..ab4270b042 100644
--- a/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx
+++ b/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx
@@ -332,7 +332,6 @@ export function AgentDefinitionDialog({
   const { data: runtimeFileConfig, isLoading: fileConfigLoading } =
     useRuntimeFileConfigQuery(runtime, { enabled: open });
   const {
-    advancedInheritedSummary,
     globalConfig,
     inheritedDefaults: {
       provider: inheritedProviderDefault,
@@ -942,12 +941,6 @@ export function AgentDefinitionDialog({
                   )}
                 />
               </button>
-              {!showAdvancedFields && advancedInheritedSummary ? (
-                <p className="text-xs text-muted-foreground">
-                  {advancedInheritedSummary}
-                </p>
-              ) : null}
-
               <AnimatePresence initial={false}>
                 {showAdvancedFields ? (
                   <motion.div
diff --git a/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx b/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx
index 1c54af73e5..7d4aa690be 100644
--- a/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx
+++ b/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx
@@ -360,7 +360,6 @@ export function AgentInstanceEditDialog({
   );
 
   const {
-    advancedInheritedSummary,
     globalConfig,
     inheritedDefaults: {
       provider: inheritedProviderDefault,
@@ -1122,12 +1121,6 @@ export function AgentInstanceEditDialog({
                   )}
                 />
               </button>
-              {!showAdvancedFields && advancedInheritedSummary ? (
-                <p className="text-xs text-muted-foreground">
-                  {advancedInheritedSummary}
-                </p>
-              ) : null}
-
               <AnimatePresence initial={false}>
                 {showAdvancedFields ? (
                   <motion.div
diff --git a/desktop/src/features/agents/ui/useAgentDialogDefaults.ts b/desktop/src/features/agents/ui/useAgentDialogDefaults.ts
index b55b8233cc..5ede9558fe 100644
--- a/desktop/src/features/agents/ui/useAgentDialogDefaults.ts
+++ b/desktop/src/features/agents/ui/useAgentDialogDefaults.ts
@@ -3,11 +3,7 @@ import * as React from "react";
 import { useBakedBuildEnvQuery } from "../hooks";
 import { useGlobalAgentConfig } from "../useGlobalAgentConfig";
 import { BUZZ_AGENT_THINKING_EFFORT } from "./buzzAgentConfig";
-import {
-  countNonSecretInheritedEnvVars,
-  getAdvancedInheritedSummary,
-  getInheritedAgentDefaults,
-} from "./bakedEnvHelpers";
+import { getInheritedAgentDefaults } from "./bakedEnvHelpers";
 
 export function useAgentDialogDefaults({
   inheritedEnvVars = {},
@@ -29,16 +25,7 @@ export function useAgentDialogDefaults({
     }),
     [globalConfig.env_vars, inheritedDefaults.effort.value, inheritedEnvVars],
   );
-  const advancedInheritedSummary = getAdvancedInheritedSummary(
-    inheritedDefaults.effort,
-    countNonSecretInheritedEnvVars(
-      globalConfig.env_vars,
-      BUZZ_AGENT_THINKING_EFFORT,
-    ),
-  );
-
   return {
-    advancedInheritedSummary,
     globalConfig,
     inheritedDefaults,
     inheritedEnvVars: effectiveInheritedEnvVars,

From 4682f544ff0c346c443165a5af475e7fa2cb5c47 Mon Sep 17 00:00:00 2001
From: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf
 <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 18:32:45 -0700
Subject: [PATCH 09/17] Show model inheritance on agent cards

Co-authored-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
---
 desktop/src/features/agents/ui/AgentsView.tsx |   9 +-
 .../agents/ui/UnifiedAgentsSection.tsx        | 117 +++++++++---------
 2 files changed, 57 insertions(+), 69 deletions(-)

diff --git a/desktop/src/features/agents/ui/AgentsView.tsx b/desktop/src/features/agents/ui/AgentsView.tsx
index 67a1f5e957..d8a4fb46b0 100644
--- a/desktop/src/features/agents/ui/AgentsView.tsx
+++ b/desktop/src/features/agents/ui/AgentsView.tsx
@@ -24,20 +24,16 @@ import { useManagedAgentActions } from "./useManagedAgentActions";
 import { usePersonaActions } from "./usePersonaActions";
 import { useTeamActions } from "./useTeamActions";
 import { useProfilePanel } from "@/shared/context/ProfilePanelContext";
-import { useAppNavigation } from "@/app/navigation/useAppNavigation";
 import { useBakedBuildEnvQuery } from "@/features/agents/hooks";
 import { useGlobalAgentConfig } from "@/features/agents/useGlobalAgentConfig";
 import { PageHeader } from "@/shared/ui/PageHeader";
-import { formatAiDefaultsSummary } from "./AgentAiDefaults";
 import { getInheritedAgentDefaults } from "./bakedEnvHelpers";
 
 export function AgentsView() {
   const { openPersonaProfilePanel, openProfilePanel } = useProfilePanel();
-  const { goSettings } = useAppNavigation();
   const { globalConfig } = useGlobalAgentConfig();
   const { data: bakedEnv } = useBakedBuildEnvQuery({ enabled: true });
   const inheritedDefaults = getInheritedAgentDefaults(globalConfig, bakedEnv);
-  const aiDefaultsSummary = formatAiDefaultsSummary(inheritedDefaults);
   const agents = useManagedAgentActions();
   const personas = usePersonaActions();
   const teamImportInputRef = React.useRef<HTMLInputElement | null>(null);
@@ -105,10 +101,7 @@ export function AgentsView() {
           />
           <div className="flex flex-col gap-8">
             <UnifiedAgentsSection
-              aiDefaultsSummary={aiDefaultsSummary}
-              onEditAiDefaults={() => {
-                void goSettings("agents");
-              }}
+              defaultModel={inheritedDefaults.model.value}
               actionErrorMessage={agents.actionErrorMessage}
               actionNoticeMessage={agents.actionNoticeMessage}
               agents={agents.managedAgents}
diff --git a/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx b/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx
index 7d708ee8b9..fb82a15db8 100644
--- a/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx
+++ b/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx
@@ -24,7 +24,6 @@ import {
   DropdownMenuTrigger,
 } from "@/shared/ui/dropdown-menu";
 import { IdentityCardSkeleton } from "@/shared/ui/identity-card-skeleton";
-import { SectionHeader } from "@/shared/ui/PageHeader";
 import { AgentIdentityCard } from "./AgentIdentityCard";
 import { AgentRuntimeAvatarControl } from "./AgentRuntimeAvatarControl";
 import { CreateIdentityCard } from "./CreateIdentityCard";
@@ -32,7 +31,7 @@ import { PersonaActionsMenu } from "./PersonaActionsMenu";
 import { buildUnifiedGroups, pickProfileAgent } from "./unifiedAgentGroups";
 
 type UnifiedAgentsSectionProps = {
-  aiDefaultsSummary: string;
+  defaultModel: string;
   actionErrorMessage: string | null;
   actionNoticeMessage: string | null;
   agents: ManagedAgent[];
@@ -42,7 +41,6 @@ type UnifiedAgentsSectionProps = {
   startingAgentPubkey: string | null;
   startingPersonaIds: ReadonlySet<string>;
   onBulkStopRunning: () => void;
-  onEditAiDefaults: () => void;
   onOpenAgentProfile: (
     pubkey: string,
     options?: ProfilePanelOpenOptions,
@@ -76,8 +74,8 @@ const AGENT_CARD_GRID_CLASS = `${AGENT_CARD_COLUMN_CLASS} grid grid-cols-[repeat
 export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
   const {
     actionErrorMessage,
-    aiDefaultsSummary,
     actionNoticeMessage,
+    defaultModel,
     agents,
     agentsError,
     isActionPending,
@@ -85,7 +83,6 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
     startingAgentPubkey,
     startingPersonaIds,
     onBulkStopRunning,
-    onEditAiDefaults,
     onOpenAgentProfile,
     onOpenPersonaProfile,
     onStartAgent,
@@ -162,15 +159,13 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
         </div>
       ) : null}
 
-      <AgentsListHeader
+      <AgentsListActions
         agentCount={agents.length}
-        aiDefaultsSummary={aiDefaultsSummary}
         fileInputRef={fileInputRef}
         handleFileChange={handleFileChange}
         isActionPending={isActionPending}
         runningCount={runningCount}
         onBulkStopRunning={onBulkStopRunning}
-        onEditAiDefaults={onEditAiDefaults}
       />
 
       {isLoading ? <LoadingSkeleton /> : null}
@@ -196,6 +191,7 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
                     />
                   }
                   agent={profileAgent}
+                  defaultModel={defaultModel}
                   key={group.persona.id}
                   persona={group.persona}
                   startingAgentPubkey={startingAgentPubkey}
@@ -220,6 +216,7 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
             <CollapsibleAgentGroup
               agents={additionalPersonaAgents}
               collapsed={collapsed}
+              defaultModel={defaultModel}
               groupKey="__additional_persona_agents__"
               label="Additional agent instances"
               startingAgentPubkey={startingAgentPubkey}
@@ -232,6 +229,7 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
             <CollapsibleAgentGroup
               agents={unknown}
               collapsed={collapsed}
+              defaultModel={defaultModel}
               groupKey="__unknown__"
               label="Unknown Agent"
               startingAgentPubkey={startingAgentPubkey}
@@ -244,6 +242,7 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
             <CollapsibleAgentGroup
               agents={ungrouped}
               collapsed={collapsed}
+              defaultModel={defaultModel}
               groupKey="__ungrouped__"
               label="Custom agents"
               startingAgentPubkey={startingAgentPubkey}
@@ -276,6 +275,7 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
 function AgentPersonaCard({
   actions,
   agent,
+  defaultModel,
   persona,
   startingAgentPubkey,
   startingPersonaIds,
@@ -286,6 +286,7 @@ function AgentPersonaCard({
 }: {
   actions?: React.ReactNode;
   agent: ManagedAgent | undefined;
+  defaultModel: string;
   persona: AgentPersona;
   startingAgentPubkey: string | null;
   startingPersonaIds: ReadonlySet<string>;
@@ -298,7 +299,10 @@ function AgentPersonaCard({
   onStartPersona: (persona: AgentPersona) => void;
 }) {
   const title = persona.displayName;
-  const modelLabel = formatAgentModelLabel(agent?.model ?? persona.model);
+  const explicitModel = agent?.model ?? persona.model;
+  const modelLabel = explicitModel?.trim()
+    ? formatAgentModelLabel(explicitModel)
+    : formatDefaultModelLabel(defaultModel);
   const isActive = agent ? isManagedAgentActive(agent) : false;
   const profileQuery = useUserProfileQuery(agent?.pubkey);
   const avatarUrl = agent
@@ -344,7 +348,7 @@ function AgentPersonaCard({
       avatarUrl={avatarUrl}
       dataTestId={`persona-agent-row-${persona.id}`}
       label={title}
-      modelLabel={agent && isActive ? modelLabel : null}
+      modelLabel={modelLabel}
       onClick={() => {
         if (agent) {
           onOpenAgentProfile(
@@ -369,11 +373,13 @@ function AgentPersonaCard({
 
 function StandaloneAgentCard({
   agent,
+  defaultModel,
   startingAgentPubkey,
   onOpenAgentProfile,
   onStartAgent,
 }: {
   agent: ManagedAgent;
+  defaultModel: string;
   startingAgentPubkey: string | null;
   onOpenAgentProfile: (
     pubkey: string,
@@ -412,7 +418,11 @@ function StandaloneAgentCard({
       avatarUrl={profileQuery.data?.avatarUrl}
       dataTestId={`managed-agent-${agent.pubkey}`}
       label={title}
-      modelLabel={isActive ? formatAgentModelLabel(agent.model) : null}
+      modelLabel={
+        agent.model?.trim()
+          ? formatAgentModelLabel(agent.model)
+          : formatDefaultModelLabel(defaultModel)
+      }
       onClick={() => {
         onOpenAgentProfile(
           agent.pubkey,
@@ -431,6 +441,11 @@ function StandaloneAgentCard({
   );
 }
 
+function formatDefaultModelLabel(defaultModel: string) {
+  const model = defaultModel.trim();
+  return model ? `Default model (${model})` : "Default model";
+}
+
 function firstAvatarUrl(
   ...candidates: Array<string | null | undefined>
 ): string | null {
@@ -441,27 +456,23 @@ function firstAvatarUrl(
   return null;
 }
 
-function AgentsListHeader({
+function AgentsListActions({
   agentCount,
-  aiDefaultsSummary,
   fileInputRef,
   handleFileChange,
   isActionPending,
   runningCount,
   onBulkStopRunning,
-  onEditAiDefaults,
 }: {
   agentCount: number;
-  aiDefaultsSummary: string;
   fileInputRef: React.RefObject<HTMLInputElement | null>;
   handleFileChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
   isActionPending: boolean;
   runningCount: number;
   onBulkStopRunning: () => void;
-  onEditAiDefaults: () => void;
 }) {
   return (
-    <div className={AGENT_CARD_COLUMN_CLASS}>
+    <>
       <input
         accept=".agent.json,.agent.png"
         className="hidden"
@@ -469,54 +480,35 @@ function AgentsListHeader({
         ref={fileInputRef}
         type="file"
       />
-      <SectionHeader
-        title="Agents"
-        description={
-          <>
-            <span>Agents in this community.</span>
-            <span className="mt-1 flex flex-wrap items-center gap-x-1 text-xs">
-              <span>AI defaults: {aiDefaultsSummary}</span>
+      {agentCount > 0 ? (
+        <div className={`${AGENT_CARD_COLUMN_CLASS} flex justify-end`}>
+          <DropdownMenu modal={false}>
+            <DropdownMenuTrigger asChild>
               <Button
-                onClick={onEditAiDefaults}
-                size="xs"
-                type="button"
-                variant="link"
+                aria-label="Bulk actions"
+                className="h-7 w-7"
+                size="icon"
+                variant="ghost"
               >
-                Edit defaults
+                <Ellipsis className="h-4 w-4" />
               </Button>
-            </span>
-          </>
-        }
-        action={
-          agentCount > 0 ? (
-            <DropdownMenu modal={false}>
-              <DropdownMenuTrigger asChild>
-                <Button
-                  aria-label="Bulk actions"
-                  className="h-7 w-7"
-                  size="icon"
-                  variant="ghost"
-                >
-                  <Ellipsis className="h-4 w-4" />
-                </Button>
-              </DropdownMenuTrigger>
-              <DropdownMenuContent
-                align="end"
-                onCloseAutoFocus={(event) => event.preventDefault()}
+            </DropdownMenuTrigger>
+            <DropdownMenuContent
+              align="end"
+              onCloseAutoFocus={(event) => event.preventDefault()}
+            >
+              <DropdownMenuItem
+                disabled={isActionPending || runningCount === 0}
+                onClick={onBulkStopRunning}
               >
-                <DropdownMenuItem
-                  disabled={isActionPending || runningCount === 0}
-                  onClick={onBulkStopRunning}
-                >
-                  <OctagonX className="h-4 w-4" />
-                  Stop all running ({runningCount})
-                </DropdownMenuItem>
-              </DropdownMenuContent>
-            </DropdownMenu>
-          ) : undefined
-        }
-      />
-    </div>
+                <OctagonX className="h-4 w-4" />
+                Stop all running ({runningCount})
+              </DropdownMenuItem>
+            </DropdownMenuContent>
+          </DropdownMenu>
+        </div>
+      ) : null}
+    </>
   );
 }
 
@@ -595,6 +587,7 @@ function CollapsibleAgentGroup({
   label,
   agents,
   collapsed,
+  defaultModel,
   startingAgentPubkey,
   onToggle,
   onOpenAgentProfile,
@@ -604,6 +597,7 @@ function CollapsibleAgentGroup({
   label: string;
   agents: ManagedAgent[];
   collapsed: ReadonlySet<string>;
+  defaultModel: string;
   startingAgentPubkey: string | null;
   onToggle: (key: string) => void;
   onOpenAgentProfile: (
@@ -633,6 +627,7 @@ function CollapsibleAgentGroup({
           {agents.map((agent) => (
             <StandaloneAgentCard
               agent={agent}
+              defaultModel={defaultModel}
               key={agent.pubkey}
               startingAgentPubkey={startingAgentPubkey}
               onOpenAgentProfile={onOpenAgentProfile}

From d8aff2fbd7022c702e3cd9c7a226e9c9fb5aef23 Mon Sep 17 00:00:00 2001
From: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf
 <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 18:33:41 -0700
Subject: [PATCH 10/17] Fit model labels on agent cards

Co-authored-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
---
 desktop/src/features/agents/ui/AgentIdentityCard.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/desktop/src/features/agents/ui/AgentIdentityCard.tsx b/desktop/src/features/agents/ui/AgentIdentityCard.tsx
index 69f2770338..6f13a84ae8 100644
--- a/desktop/src/features/agents/ui/AgentIdentityCard.tsx
+++ b/desktop/src/features/agents/ui/AgentIdentityCard.tsx
@@ -73,7 +73,7 @@ export function AgentIdentityCard({
           {label}
         </span>
         {modelLabel ? (
-          <span className="min-w-0 truncate font-normal text-secondary-foreground/75">
+          <span className="min-w-0 truncate text-xs font-normal text-secondary-foreground/75">
             {modelLabel}
           </span>
         ) : null}

From 3ad3ec9644650bc9e35a067f95b236cf089af088 Mon Sep 17 00:00:00 2001
From: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf
 <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 19:03:50 -0700
Subject: [PATCH 11/17] Put agent settings before AI defaults

Co-authored-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
---
 desktop/src/features/settings/ui/SettingsPanels.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/desktop/src/features/settings/ui/SettingsPanels.tsx b/desktop/src/features/settings/ui/SettingsPanels.tsx
index 0abd4ddda2..767d758946 100644
--- a/desktop/src/features/settings/ui/SettingsPanels.tsx
+++ b/desktop/src/features/settings/ui/SettingsPanels.tsx
@@ -715,8 +715,8 @@ export function renderSettingsSection(
     case "agents":
       return (
         <div className="space-y-12">
-          <GlobalAgentConfigSettingsCard />
           <PreventSleepSettingsCard />
+          <GlobalAgentConfigSettingsCard />
         </div>
       );
     case "channel-templates":

From ff0b24be2b3fb169b3aaeeb25c2330e97b2e1fbf Mon Sep 17 00:00:00 2001
From: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf
 <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 19:13:59 -0700
Subject: [PATCH 12/17] Add shared bottom space to settings pages

Co-authored-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
---
 desktop/src/features/settings/ui/SettingsView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/desktop/src/features/settings/ui/SettingsView.tsx b/desktop/src/features/settings/ui/SettingsView.tsx
index 1a2dbae2ec..d32864c555 100644
--- a/desktop/src/features/settings/ui/SettingsView.tsx
+++ b/desktop/src/features/settings/ui/SettingsView.tsx
@@ -277,7 +277,7 @@ export function SettingsView({
           data-tauri-drag-region
         />
         <div className="relative z-10 ml-px mt-px flex min-h-0 flex-1 flex-col overflow-hidden rounded-tl-xl bg-background pt-11 shadow-[-1px_-1px_0_0_hsl(var(--sidebar-border)/0.45)]">
-          <section className="min-h-0 flex-1 overflow-y-auto px-5 pb-5 pt-4 sm:px-6">
+          <section className="min-h-0 flex-1 overflow-y-auto px-5 pb-12 pt-4 sm:px-6">
             <div
               className="mx-auto flex h-full w-full max-w-4xl flex-col gap-4"
               data-testid={`settings-panel-${section}`}

From 9421e6f561e837769706a88632d910ced5ee8b0e Mon Sep 17 00:00:00 2001
From: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf
 <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 19:23:55 -0700
Subject: [PATCH 13/17] feat(desktop): expose global model on agents page

Co-authored-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
---
 .../agents/ui/AgentAiDefaultsDialog.tsx       |  3 +-
 desktop/src/features/agents/ui/AgentsView.tsx | 12 +++
 .../agents/ui/UnifiedAgentsSection.tsx        | 77 ++++++++++---------
 3 files changed, 52 insertions(+), 40 deletions(-)

diff --git a/desktop/src/features/agents/ui/AgentAiDefaultsDialog.tsx b/desktop/src/features/agents/ui/AgentAiDefaultsDialog.tsx
index 22a8ba766e..bc9f947386 100644
--- a/desktop/src/features/agents/ui/AgentAiDefaultsDialog.tsx
+++ b/desktop/src/features/agents/ui/AgentAiDefaultsDialog.tsx
@@ -127,8 +127,7 @@ export function AgentAiDefaultsDialog({
           <AlertDialogHeader>
             <AlertDialogTitle>Discard changes to AI defaults?</AlertDialogTitle>
             <AlertDialogDescription>
-              Your agent draft will stay open, but changes made in this overlay
-              will be lost.
+              Unsaved changes made to AI defaults will be lost.
             </AlertDialogDescription>
           </AlertDialogHeader>
           <AlertDialogFooter>
diff --git a/desktop/src/features/agents/ui/AgentsView.tsx b/desktop/src/features/agents/ui/AgentsView.tsx
index d8a4fb46b0..33e35b12ce 100644
--- a/desktop/src/features/agents/ui/AgentsView.tsx
+++ b/desktop/src/features/agents/ui/AgentsView.tsx
@@ -5,6 +5,7 @@ import {
 } from "@/features/agents/openSnapshotImportFromUrlEvent";
 import { AddAgentToChannelDialog } from "./AddAgentToChannelDialog";
 import { AddTeamToChannelDialog } from "./AddTeamToChannelDialog";
+import { AgentAiDefaultsDialog } from "./AgentAiDefaultsDialog";
 import { AgentDialog } from "./AgentDialog";
 import { PersonaCatalogDialog } from "./PersonaCatalogDialog";
 import { PersonaDeleteDialog } from "./PersonaDeleteDialog";
@@ -37,6 +38,8 @@ export function AgentsView() {
   const agents = useManagedAgentActions();
   const personas = usePersonaActions();
   const teamImportInputRef = React.useRef<HTMLInputElement | null>(null);
+  const aiDefaultsTriggerRef = React.useRef<HTMLButtonElement>(null);
+  const [isAiDefaultsOpen, setIsAiDefaultsOpen] = React.useState(false);
   // Exclusivity: create never sets `personaDialogState` (edit/dup/import do),
   // so the create-mode and definition-edit AgentDialog mounts never coexist.
   const [isCreateDialogOpen, setIsCreateDialogOpen] = React.useState(false);
@@ -102,6 +105,8 @@ export function AgentsView() {
           <div className="flex flex-col gap-8">
             <UnifiedAgentsSection
               defaultModel={inheritedDefaults.model.value}
+              globalModel={globalConfig.model}
+              globalModelTriggerRef={aiDefaultsTriggerRef}
               actionErrorMessage={agents.actionErrorMessage}
               actionNoticeMessage={agents.actionNoticeMessage}
               agents={agents.managedAgents}
@@ -117,6 +122,7 @@ export function AgentsView() {
               onBulkStopRunning={() => {
                 void agents.handleBulkStopRunning();
               }}
+              onEditGlobalModel={() => setIsAiDefaultsOpen(true)}
               onOpenAgentProfile={(pubkey, options) => {
                 openProfilePanel?.(pubkey, options);
               }}
@@ -204,6 +210,12 @@ export function AgentsView() {
         </div>
       </div>
 
+      <AgentAiDefaultsDialog
+        onOpenChange={setIsAiDefaultsOpen}
+        open={isAiDefaultsOpen}
+        returnFocusRef={aiDefaultsTriggerRef}
+      />
+
       {isCreateDialogOpen ? (
         <AgentDialog
           definitionError={
diff --git a/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx b/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx
index fb82a15db8..801adab2a1 100644
--- a/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx
+++ b/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx
@@ -1,11 +1,5 @@
 import * as React from "react";
-import {
-  ChevronDown,
-  ChevronRight,
-  Ellipsis,
-  OctagonX,
-  RefreshCw,
-} from "lucide-react";
+import { ChevronDown, ChevronRight, OctagonX, RefreshCw } from "lucide-react";
 
 import { formatAgentModelLabel } from "@/features/agents/lib/formatAgentModelLabel";
 import { friendlyAgentLastError } from "@/features/agents/lib/friendlyAgentLastError";
@@ -32,6 +26,8 @@ import { buildUnifiedGroups, pickProfileAgent } from "./unifiedAgentGroups";
 
 type UnifiedAgentsSectionProps = {
   defaultModel: string;
+  globalModel: string | null;
+  globalModelTriggerRef: React.RefObject<HTMLButtonElement | null>;
   actionErrorMessage: string | null;
   actionNoticeMessage: string | null;
   agents: ManagedAgent[];
@@ -41,6 +37,7 @@ type UnifiedAgentsSectionProps = {
   startingAgentPubkey: string | null;
   startingPersonaIds: ReadonlySet<string>;
   onBulkStopRunning: () => void;
+  onEditGlobalModel: () => void;
   onOpenAgentProfile: (
     pubkey: string,
     options?: ProfilePanelOpenOptions,
@@ -83,6 +80,9 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
     startingAgentPubkey,
     startingPersonaIds,
     onBulkStopRunning,
+    globalModel,
+    globalModelTriggerRef,
+    onEditGlobalModel,
     onOpenAgentProfile,
     onOpenPersonaProfile,
     onStartAgent,
@@ -160,12 +160,14 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
       ) : null}
 
       <AgentsListActions
-        agentCount={agents.length}
         fileInputRef={fileInputRef}
+        globalModel={globalModel}
+        globalModelTriggerRef={globalModelTriggerRef}
         handleFileChange={handleFileChange}
         isActionPending={isActionPending}
         runningCount={runningCount}
         onBulkStopRunning={onBulkStopRunning}
+        onEditGlobalModel={onEditGlobalModel}
       />
 
       {isLoading ? <LoadingSkeleton /> : null}
@@ -457,20 +459,26 @@ function firstAvatarUrl(
 }
 
 function AgentsListActions({
-  agentCount,
   fileInputRef,
+  globalModel,
+  globalModelTriggerRef,
   handleFileChange,
   isActionPending,
   runningCount,
   onBulkStopRunning,
+  onEditGlobalModel,
 }: {
-  agentCount: number;
   fileInputRef: React.RefObject<HTMLInputElement | null>;
+  globalModel: string | null;
+  globalModelTriggerRef: React.RefObject<HTMLButtonElement | null>;
   handleFileChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
   isActionPending: boolean;
   runningCount: number;
   onBulkStopRunning: () => void;
+  onEditGlobalModel: () => void;
 }) {
+  const model = globalModel?.trim();
+
   return (
     <>
       <input
@@ -480,34 +488,27 @@ function AgentsListActions({
         ref={fileInputRef}
         type="file"
       />
-      {agentCount > 0 ? (
-        <div className={`${AGENT_CARD_COLUMN_CLASS} flex justify-end`}>
-          <DropdownMenu modal={false}>
-            <DropdownMenuTrigger asChild>
-              <Button
-                aria-label="Bulk actions"
-                className="h-7 w-7"
-                size="icon"
-                variant="ghost"
-              >
-                <Ellipsis className="h-4 w-4" />
-              </Button>
-            </DropdownMenuTrigger>
-            <DropdownMenuContent
-              align="end"
-              onCloseAutoFocus={(event) => event.preventDefault()}
-            >
-              <DropdownMenuItem
-                disabled={isActionPending || runningCount === 0}
-                onClick={onBulkStopRunning}
-              >
-                <OctagonX className="h-4 w-4" />
-                Stop all running ({runningCount})
-              </DropdownMenuItem>
-            </DropdownMenuContent>
-          </DropdownMenu>
-        </div>
-      ) : null}
+      <div className={`${AGENT_CARD_COLUMN_CLASS} flex flex-wrap gap-2`}>
+        <Button
+          onClick={onEditGlobalModel}
+          ref={globalModelTriggerRef}
+          size="sm"
+          variant="outline"
+        >
+          {model ? `Global model: ${model}` : "Set global model"}
+        </Button>
+        {runningCount > 0 ? (
+          <Button
+            disabled={isActionPending}
+            onClick={onBulkStopRunning}
+            size="sm"
+            variant="outline"
+          >
+            <OctagonX />
+            Stop all running agents
+          </Button>
+        ) : null}
+      </div>
     </>
   );
 }

From f970db5e351bc1db7b2266855d41b20dbcccd42f Mon Sep 17 00:00:00 2001
From: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf
 <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 19:29:58 -0700
Subject: [PATCH 14/17] fix(desktop): align agent actions with page header

Co-authored-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1s654z6utm59mkg4tjdqw69tlen05y2hrrwajlck4n2ar0jyx7hfsmp60kf <86a9516b8bdd0bbb22ab9340ed157fccdf422ae31bbb2fe2d59aba37c886f5d3@sprout-oss.stage.blox.sqprod.co>
---
 desktop/src/features/agents/ui/AgentsView.tsx | 40 +++++++--
 .../agents/ui/UnifiedAgentsSection.tsx        | 84 ++-----------------
 2 files changed, 41 insertions(+), 83 deletions(-)

diff --git a/desktop/src/features/agents/ui/AgentsView.tsx b/desktop/src/features/agents/ui/AgentsView.tsx
index 33e35b12ce..c3229e9f5b 100644
--- a/desktop/src/features/agents/ui/AgentsView.tsx
+++ b/desktop/src/features/agents/ui/AgentsView.tsx
@@ -1,4 +1,5 @@
 import * as React from "react";
+import { OctagonX } from "lucide-react";
 import {
   consumePendingSnapshotImport,
   subscribeSnapshotImport,
@@ -26,7 +27,9 @@ import { usePersonaActions } from "./usePersonaActions";
 import { useTeamActions } from "./useTeamActions";
 import { useProfilePanel } from "@/shared/context/ProfilePanelContext";
 import { useBakedBuildEnvQuery } from "@/features/agents/hooks";
+import { isManagedAgentActive } from "@/features/agents/lib/managedAgentControlActions";
 import { useGlobalAgentConfig } from "@/features/agents/useGlobalAgentConfig";
+import { Button } from "@/shared/ui/button";
 import { PageHeader } from "@/shared/ui/PageHeader";
 import { getInheritedAgentDefaults } from "./bakedEnvHelpers";
 
@@ -65,6 +68,10 @@ export function AgentsView() {
     teamActions.createTeamMutation.isPending ||
     teamActions.updateTeamMutation.isPending ||
     teamActions.deleteTeamMutation.isPending;
+  const runningAgentCount = agents.managedAgents.filter((agent) =>
+    isManagedAgentActive(agent),
+  ).length;
+  const configuredGlobalModel = globalConfig.model?.trim();
 
   // biome-ignore lint/correctness/useExhaustiveDependencies: mount-only; personas.handleImportSnapshotFile and teamActions.handleImportTeamSnapshotFile are stable
   React.useEffect(() => {
@@ -99,14 +106,39 @@ export function AgentsView() {
       <div className="flex-1 overflow-y-auto overflow-x-hidden overscroll-contain px-4 py-7 sm:px-6 sm:py-8">
         <div className="mx-auto flex w-full max-w-6xl flex-col gap-8">
           <PageHeader
+            action={
+              <div className="flex flex-wrap justify-end gap-2">
+                <Button
+                  onClick={() => setIsAiDefaultsOpen(true)}
+                  ref={aiDefaultsTriggerRef}
+                  size="sm"
+                  variant="outline"
+                >
+                  {configuredGlobalModel
+                    ? `Global model: ${configuredGlobalModel}`
+                    : "Set global model"}
+                </Button>
+                {runningAgentCount > 0 ? (
+                  <Button
+                    disabled={isActionPending}
+                    onClick={() => {
+                      void agents.handleBulkStopRunning();
+                    }}
+                    size="sm"
+                    variant="outline"
+                  >
+                    <OctagonX />
+                    Stop running agents
+                  </Button>
+                ) : null}
+              </div>
+            }
             description="Set up and manage your agents."
             title="Agents"
           />
           <div className="flex flex-col gap-8">
             <UnifiedAgentsSection
               defaultModel={inheritedDefaults.model.value}
-              globalModel={globalConfig.model}
-              globalModelTriggerRef={aiDefaultsTriggerRef}
               actionErrorMessage={agents.actionErrorMessage}
               actionNoticeMessage={agents.actionNoticeMessage}
               agents={agents.managedAgents}
@@ -119,10 +151,6 @@ export function AgentsView() {
               isAgentsLoading={agents.managedAgentsQuery.isLoading}
               startingAgentPubkey={agents.startingAgentPubkey}
               startingPersonaIds={agents.startingPersonaIds}
-              onBulkStopRunning={() => {
-                void agents.handleBulkStopRunning();
-              }}
-              onEditGlobalModel={() => setIsAiDefaultsOpen(true)}
               onOpenAgentProfile={(pubkey, options) => {
                 openProfilePanel?.(pubkey, options);
               }}
diff --git a/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx b/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx
index 801adab2a1..04bbde79ef 100644
--- a/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx
+++ b/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx
@@ -1,5 +1,5 @@
 import * as React from "react";
-import { ChevronDown, ChevronRight, OctagonX, RefreshCw } from "lucide-react";
+import { ChevronDown, ChevronRight, RefreshCw } from "lucide-react";
 
 import { formatAgentModelLabel } from "@/features/agents/lib/formatAgentModelLabel";
 import { friendlyAgentLastError } from "@/features/agents/lib/friendlyAgentLastError";
@@ -10,7 +10,6 @@ import type { ProfilePanelOpenOptions } from "@/shared/context/ProfilePanelConte
 import { useFeedbackToasts } from "@/shared/hooks/useToastEffect";
 import { useFileImportZone } from "@/shared/hooks/useFileImportZone";
 import { Badge } from "@/shared/ui/badge";
-import { Button } from "@/shared/ui/button";
 import {
   DropdownMenu,
   DropdownMenuContent,
@@ -26,8 +25,6 @@ import { buildUnifiedGroups, pickProfileAgent } from "./unifiedAgentGroups";
 
 type UnifiedAgentsSectionProps = {
   defaultModel: string;
-  globalModel: string | null;
-  globalModelTriggerRef: React.RefObject<HTMLButtonElement | null>;
   actionErrorMessage: string | null;
   actionNoticeMessage: string | null;
   agents: ManagedAgent[];
@@ -36,8 +33,6 @@ type UnifiedAgentsSectionProps = {
   isAgentsLoading: boolean;
   startingAgentPubkey: string | null;
   startingPersonaIds: ReadonlySet<string>;
-  onBulkStopRunning: () => void;
-  onEditGlobalModel: () => void;
   onOpenAgentProfile: (
     pubkey: string,
     options?: ProfilePanelOpenOptions,
@@ -79,10 +74,6 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
     isAgentsLoading,
     startingAgentPubkey,
     startingPersonaIds,
-    onBulkStopRunning,
-    globalModel,
-    globalModelTriggerRef,
-    onEditGlobalModel,
     onOpenAgentProfile,
     onOpenPersonaProfile,
     onStartAgent,
@@ -104,9 +95,6 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
     onImportSnapshotFile,
   } = props;
 
-  const runningCount = agents.filter((agent) =>
-    isManagedAgentActive(agent),
-  ).length;
   const { groups, ungrouped, unknown } = React.useMemo(
     () => buildUnifiedGroups(personas, agents),
     [personas, agents],
@@ -159,15 +147,12 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) {
         </div>
       ) : null}
 
-      <AgentsListActions
-        fileInputRef={fileInputRef}
-        globalModel={globalModel}
-        globalModelTriggerRef={globalModelTriggerRef}
-        handleFileChange={handleFileChange}
-        isActionPending={isActionPending}
-        runningCount={runningCount}
-        onBulkStopRunning={onBulkStopRunning}
-        onEditGlobalModel={onEditGlobalModel}
+      <input
+        accept=".agent.json,.agent.png"
+        className="hidden"
+        onChange={handleFileChange}
+        ref={fileInputRef}
+        type="file"
       />
 
       {isLoading ? <LoadingSkeleton /> : null}
@@ -458,61 +443,6 @@ function firstAvatarUrl(
   return null;
 }
 
-function AgentsListActions({
-  fileInputRef,
-  globalModel,
-  globalModelTriggerRef,
-  handleFileChange,
-  isActionPending,
-  runningCount,
-  onBulkStopRunning,
-  onEditGlobalModel,
-}: {
-  fileInputRef: React.RefObject<HTMLInputElement | null>;
-  globalModel: string | null;
-  globalModelTriggerRef: React.RefObject<HTMLButtonElement | null>;
-  handleFileChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
-  isActionPending: boolean;
-  runningCount: number;
-  onBulkStopRunning: () => void;
-  onEditGlobalModel: () => void;
-}) {
-  const model = globalModel?.trim();
-
-  return (
-    <>
-      <input
-        accept=".agent.json,.agent.png"
-        className="hidden"
-        onChange={handleFileChange}
-        ref={fileInputRef}
-        type="file"
-      />
-      <div className={`${AGENT_CARD_COLUMN_CLASS} flex flex-wrap gap-2`}>
-        <Button
-          onClick={onEditGlobalModel}
-          ref={globalModelTriggerRef}
-          size="sm"
-          variant="outline"
-        >
-          {model ? `Global model: ${model}` : "Set global model"}
-        </Button>
-        {runningCount > 0 ? (
-          <Button
-            disabled={isActionPending}
-            onClick={onBulkStopRunning}
-            size="sm"
-            variant="outline"
-          >
-            <OctagonX />
-            Stop all running agents
-          </Button>
-        ) : null}
-      </div>
-    </>
-  );
-}
-
 function NewAgentCard({
   canChooseCatalog,
   isPersonasPending,

From ee5642b44b7eb800a4351bd2231e2c9dd05a0dd1 Mon Sep 17 00:00:00 2001
From: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex
 <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 19:47:42 -0700
Subject: [PATCH 15/17] test(desktop): align agent smoke tests with AI modes

Co-authored-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
---
 desktop/tests/e2e/edit-agent.spec.ts          | 14 ++++++--
 .../global-agent-config-screenshots.spec.ts   | 36 +++++++++++--------
 ...persona-model-combobox-screenshots.spec.ts |  1 +
 3 files changed, 35 insertions(+), 16 deletions(-)

diff --git a/desktop/tests/e2e/edit-agent.spec.ts b/desktop/tests/e2e/edit-agent.spec.ts
index 216edea7f3..e04980aff6 100644
--- a/desktop/tests/e2e/edit-agent.spec.ts
+++ b/desktop/tests/e2e/edit-agent.spec.ts
@@ -174,8 +174,12 @@ test.describe("edit agent dialog", () => {
     await expect(page.locator("#edit-agent-model")).toHaveText(
       "Inherit build default (claude-opus-4-8)",
     );
+    const defaults = page.getByTestId("agent-ai-defaults-notice");
     await expect(
-      page.getByText("Using build defaults: effort high"),
+      defaults.getByText("Anthropic", { exact: true }),
+    ).toBeVisible();
+    await expect(
+      defaults.getByText("claude-opus-4-8", { exact: true }),
     ).toBeVisible();
   });
 
@@ -207,7 +211,13 @@ test.describe("edit agent dialog", () => {
     await expect(page.locator("#edit-agent-model")).toHaveText(
       "Use AI defaults (claude-opus-4-5)",
     );
-    await expect(page.getByText("Using AI defaults: effort low")).toBeVisible();
+    const defaults = page.getByTestId("agent-ai-defaults-notice");
+    await expect(
+      defaults.getByText("Anthropic", { exact: true }),
+    ).toBeVisible();
+    await expect(
+      defaults.getByText("claude-opus-4-5", { exact: true }),
+    ).toBeVisible();
   });
 
   test("profile Edit routes persona-linked agents to the definition editor", async ({
diff --git a/desktop/tests/e2e/global-agent-config-screenshots.spec.ts b/desktop/tests/e2e/global-agent-config-screenshots.spec.ts
index 85b15aa7a8..36aefa9788 100644
--- a/desktop/tests/e2e/global-agent-config-screenshots.spec.ts
+++ b/desktop/tests/e2e/global-agent-config-screenshots.spec.ts
@@ -42,6 +42,10 @@ async function openCreateDialog(page: import("@playwright/test").Page) {
   await page.locator("#persona-display-name").fill("Test Agent");
 }
 
+async function customizeAgentAi(page: import("@playwright/test").Page) {
+  await page.getByRole("tab", { name: "Customize for this agent" }).click();
+}
+
 test.describe("global agent config screenshots", () => {
   test.use({ viewport: { width: 1280, height: 900 } });
 
@@ -90,6 +94,7 @@ test.describe("global agent config screenshots", () => {
     });
 
     await openCreateDialog(page);
+    await customizeAgentAi(page);
 
     await expect(page.getByLabel("Anthropic API Key")).toBeVisible({
       timeout: 10_000,
@@ -110,6 +115,7 @@ test.describe("global agent config screenshots", () => {
     });
 
     await openCreateDialog(page);
+    await customizeAgentAi(page);
 
     await expect(page.getByLabel("Anthropic API Key")).toHaveAttribute(
       "placeholder",
@@ -151,15 +157,15 @@ test.describe("global agent config screenshots", () => {
 
     await openCreateDialog(page);
 
-    await expect(page.locator("#persona-llm-provider")).toHaveText(
-      "Anthropic (inherited from build)",
-    );
-    await expect(page.locator("#persona-model")).toHaveText(
-      "Inherit build default (claude-opus-4-8)",
-    );
+    const defaults = page.getByTestId("agent-ai-defaults-notice");
+    await expect(
+      defaults.getByText("Anthropic", { exact: true }),
+    ).toBeVisible();
     await expect(
-      page.getByText("Using build defaults: effort high"),
+      defaults.getByText("claude-opus-4-8", { exact: true }),
     ).toBeVisible();
+    await expect(page.locator("#persona-llm-provider")).not.toBeVisible();
+    await expect(page.locator("#persona-model")).not.toBeVisible();
   });
 
   test("07-explicit-global-defaults-override-baked-labels", async ({
@@ -188,13 +194,15 @@ test.describe("global agent config screenshots", () => {
 
     await openCreateDialog(page);
 
-    await expect(page.locator("#persona-llm-provider")).toHaveText(
-      "Use AI defaults (anthropic)",
-    );
-    await expect(page.locator("#persona-model")).toHaveText(
-      "Use AI defaults (claude-opus-4-5)",
-    );
-    await expect(page.getByText("Using AI defaults: effort low")).toBeVisible();
+    const defaults = page.getByTestId("agent-ai-defaults-notice");
+    await expect(
+      defaults.getByText("Anthropic", { exact: true }),
+    ).toBeVisible();
+    await expect(
+      defaults.getByText("claude-opus-4-5", { exact: true }),
+    ).toBeVisible();
+    await expect(page.locator("#persona-llm-provider")).not.toBeVisible();
+    await expect(page.locator("#persona-model")).not.toBeVisible();
   });
 
   // Shot 04: Create gate BLOCKED — no per-agent provider, no global provider
diff --git a/desktop/tests/e2e/persona-model-combobox-screenshots.spec.ts b/desktop/tests/e2e/persona-model-combobox-screenshots.spec.ts
index d57c9edb06..e3844bfacb 100644
--- a/desktop/tests/e2e/persona-model-combobox-screenshots.spec.ts
+++ b/desktop/tests/e2e/persona-model-combobox-screenshots.spec.ts
@@ -40,6 +40,7 @@ async function openNewPersonaDialog(page: import("@playwright/test").Page) {
 
   const dialog = page.getByTestId("persona-dialog");
   await expect(dialog).toBeVisible({ timeout: 8_000 });
+  await dialog.getByRole("tab", { name: "Customize for this agent" }).click();
   return dialog;
 }
 

From d3ce270e09181f0750a7053726c111a3f60b0363 Mon Sep 17 00:00:00 2001
From: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex
 <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 20:07:56 -0700
Subject: [PATCH 16/17] test(desktop): update remaining agent smoke flows

Co-authored-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
---
 desktop/tests/e2e/smoke.spec.ts               | 19 ++++++++++---------
 .../welcome-agent-modal-screenshots.spec.ts   |  1 +
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/desktop/tests/e2e/smoke.spec.ts b/desktop/tests/e2e/smoke.spec.ts
index 1a84cd42a0..8fde554fe9 100644
--- a/desktop/tests/e2e/smoke.spec.ts
+++ b/desktop/tests/e2e/smoke.spec.ts
@@ -99,6 +99,7 @@ test("loads the app shell with mocked channels", async ({ page }) => {
 async function chooseSharedComputeProvider(
   page: import("@playwright/test").Page,
 ) {
+  await page.getByRole("tab", { name: "Customize for this agent" }).click();
   const provider = page.locator("#persona-llm-provider");
   await expect(provider).toBeVisible({ timeout: 10_000 });
   await provider.press("Enter");
@@ -125,7 +126,7 @@ test("creates a new mocked stream", async ({ page }) => {
   await expect(page.getByTestId("chat-title")).toContainText(channelName);
 });
 
-test("Buzz shared compute explains the empty state without blocking Default auto", async ({
+test("Buzz shared compute explains automatic model selection", async ({
   page,
 }) => {
   await page.goto("/");
@@ -152,13 +153,12 @@ test("Buzz shared compute explains the empty state without blocking Default auto
       ),
     )
     .toContain("discover_agent_models");
-  await expect(page.locator("#persona-model")).toContainText("Default (auto)");
+  await expect(page.locator("#persona-model")).toContainText("Automatic");
   await expect(
-    page.getByTestId("persona-model-discovery-status"),
-  ).toContainText("No members are sharing compute right now");
-  await expect(
-    page.getByTestId("persona-model-discovery-status"),
-  ).toContainText("Settings > Compute");
+    page.getByText(
+      "Buzz will choose an available shared model when the agent starts.",
+    ),
+  ).toBeVisible();
   await expect(page.locator("#persona-custom-model")).toHaveCount(0);
 });
 
@@ -176,7 +176,7 @@ test("create agent persists Buzz shared compute with auto model", async ({
   await chooseSharedComputeProvider(page);
 
   const model = page.locator("#persona-model");
-  await expect(model).toContainText("Default (auto)");
+  await expect(model).toContainText("Automatic");
   await page.getByTestId("persona-dialog-submit").click();
   await expect(
     page.getByRole("heading", { name: "Agent created" }),
@@ -222,7 +222,8 @@ test("create agent supports parallelism and system prompt overrides", async ({
     .fill("You are concise and parallelize independent work.");
 
   // The buzz-agent runtime auto-selects once the ACP runtime catalog loads;
-  // the LLM provider field renders after that.
+  // Customize reveals the per-agent LLM provider and model fields.
+  await page.getByRole("tab", { name: "Customize for this agent" }).click();
   const llmProvider = page.locator("#persona-llm-provider");
   await expect(llmProvider).toBeVisible({ timeout: 10_000 });
   await llmProvider.press("Enter");
diff --git a/desktop/tests/e2e/welcome-agent-modal-screenshots.spec.ts b/desktop/tests/e2e/welcome-agent-modal-screenshots.spec.ts
index 69f89dee67..723da25dcc 100644
--- a/desktop/tests/e2e/welcome-agent-modal-screenshots.spec.ts
+++ b/desktop/tests/e2e/welcome-agent-modal-screenshots.spec.ts
@@ -153,6 +153,7 @@ test.describe("welcome and channel agent entry points", () => {
     await page
       .locator("#persona-system-prompt")
       .fill("Research a topic and return a concise brief.");
+    await page.getByRole("tab", { name: "Customize for this agent" }).click();
     const provider = page.locator("#persona-llm-provider");
     await provider.press("Enter");
     await page

From 6147a1e75a89565a81c4d18b12eddf9c0701fd38 Mon Sep 17 00:00:00 2001
From: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex
 <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Date: Wed, 15 Jul 2026 20:50:49 -0700
Subject: [PATCH 17/17] test(desktop): finish AI mode test migration

Co-authored-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1d6t84ajeg9skp2609l2k6axgcme8x7g7u7luj352r03hcwreg7lqnxcsex <6e967af659416160ab4f2fd56d74c8c6f273791ee7bfc9468a1be37c387947be@sprout-oss.stage.blox.sqprod.co>
---
 .../e2e/agent-lifecycle-feedback.spec.ts      | 51 ++++++++++---
 .../e2e/agent-provider-dropdowns.spec.ts      |  2 +
 .../e2e/agent-readiness-screenshots.spec.ts   | 71 +++++++++++--------
 desktop/tests/e2e/agents.spec.ts              |  4 --
 desktop/tests/e2e/persona-env-vars.spec.ts    | 18 ++---
 5 files changed, 93 insertions(+), 53 deletions(-)

diff --git a/desktop/tests/e2e/agent-lifecycle-feedback.spec.ts b/desktop/tests/e2e/agent-lifecycle-feedback.spec.ts
index 0a3ec16f75..05d6b3c9b1 100644
--- a/desktop/tests/e2e/agent-lifecycle-feedback.spec.ts
+++ b/desktop/tests/e2e/agent-lifecycle-feedback.spec.ts
@@ -50,6 +50,13 @@ async function openAiDefaultsSettings(page: import("@playwright/test").Page) {
   });
 }
 
+async function makeGlobalDefaultsDirty(
+  page: import("@playwright/test").Page,
+  effort = "high",
+) {
+  await page.locator("#global-agent-thinking-effort").selectOption(effort);
+}
+
 test.describe("agent lifecycle feedback screenshots", () => {
   test.use({ viewport: { width: 1280, height: 900 } });
 
@@ -126,6 +133,11 @@ test.describe("agent lifecycle feedback screenshots", () => {
   // restart-count feedback.
   test("02-save-restarted", async ({ page }) => {
     await installMockBridge(page, {
+      globalAgentConfig: {
+        provider: "anthropic",
+        model: "claude-opus-4-5",
+        env_vars: {},
+      },
       globalConfigRestartedCount: 2,
     });
 
@@ -134,7 +146,7 @@ test.describe("agent lifecycle feedback screenshots", () => {
     const card = page.getByTestId("settings-global-agent-config");
 
     // Change the provider to make the card dirty (enables "Save defaults").
-    await page.locator("#global-agent-provider").selectOption("anthropic");
+    await makeGlobalDefaultsDirty(page);
     await expect(
       page.getByRole("button", { name: "Save defaults" }),
     ).toBeEnabled({ timeout: 5_000 });
@@ -157,14 +169,20 @@ test.describe("agent lifecycle feedback screenshots", () => {
   // The default mock returns restarted_count=0. The old text
   // "Running agents keep their current settings until restarted." must be absent.
   test("03-save-plain", async ({ page }) => {
-    await installMockBridge(page);
+    await installMockBridge(page, {
+      globalAgentConfig: {
+        provider: "anthropic",
+        model: "claude-opus-4-5",
+        env_vars: {},
+      },
+    });
 
     await openAiDefaultsSettings(page);
 
     const card = page.getByTestId("settings-global-agent-config");
 
     // Change the provider to make the card dirty.
-    await page.locator("#global-agent-provider").selectOption("anthropic");
+    await makeGlobalDefaultsDirty(page);
     await expect(
       page.getByRole("button", { name: "Save defaults" }),
     ).toBeEnabled({ timeout: 5_000 });
@@ -270,6 +288,11 @@ test.describe("agent lifecycle feedback screenshots", () => {
   // Shot 06: global config save — singular "Saved. Restarted 1 agent."
   test("06-save-restarted-singular", async ({ page }) => {
     await installMockBridge(page, {
+      globalAgentConfig: {
+        provider: "anthropic",
+        model: "claude-opus-4-5",
+        env_vars: {},
+      },
       globalConfigRestartedCount: 1,
     });
 
@@ -277,7 +300,7 @@ test.describe("agent lifecycle feedback screenshots", () => {
 
     const card = page.getByTestId("settings-global-agent-config");
 
-    await page.locator("#global-agent-provider").selectOption("anthropic");
+    await makeGlobalDefaultsDirty(page);
     await expect(
       page.getByRole("button", { name: "Save defaults" }),
     ).toBeEnabled({ timeout: 5_000 });
@@ -293,6 +316,11 @@ test.describe("agent lifecycle feedback screenshots", () => {
   // Shot 07: global config save — partial failure "M failed to restart".
   test("07-save-failed-restart", async ({ page }) => {
     await installMockBridge(page, {
+      globalAgentConfig: {
+        provider: "anthropic",
+        model: "claude-opus-4-5",
+        env_vars: {},
+      },
       globalConfigFailedRestartCount: 1,
     });
 
@@ -300,7 +328,7 @@ test.describe("agent lifecycle feedback screenshots", () => {
 
     const card = page.getByTestId("settings-global-agent-config");
 
-    await page.locator("#global-agent-provider").selectOption("anthropic");
+    await makeGlobalDefaultsDirty(page);
     await expect(
       page.getByRole("button", { name: "Save defaults" }),
     ).toBeEnabled({ timeout: 5_000 });
@@ -325,28 +353,33 @@ test.describe("agent lifecycle feedback screenshots", () => {
   // clobbering it with the older response.
   test("08-save-race-keeps-newer-edit", async ({ page }) => {
     await installMockBridge(page, {
+      globalAgentConfig: {
+        provider: "anthropic",
+        model: "claude-opus-4-5",
+        env_vars: {},
+      },
       globalConfigSaveDelayMs: 2_000,
     });
 
     await openAiDefaultsSettings(page);
 
     const card = page.getByTestId("settings-global-agent-config");
-    const provider = page.locator("#global-agent-provider");
+    const effort = page.locator("#global-agent-thinking-effort");
     const saveButton = page.getByRole("button", { name: "Save defaults" });
 
-    await provider.selectOption("anthropic");
+    await makeGlobalDefaultsDirty(page, "high");
     await expect(saveButton).toBeEnabled({ timeout: 5_000 });
     await saveButton.click();
 
     // While the save is held open by the mock delay, make a newer edit.
-    await provider.selectOption("openai");
+    await effort.selectOption("low");
 
     // The save resolves with the OLD submitted config; the newer edit must
     // survive and the card must stay dirty so it can be saved again.
     await expect(card.getByText("Saved.", { exact: true })).toBeVisible({
       timeout: 10_000,
     });
-    await expect(provider).toHaveValue("openai");
+    await expect(effort).toHaveValue("low");
     await expect(saveButton).toBeEnabled();
   });
 });
diff --git a/desktop/tests/e2e/agent-provider-dropdowns.spec.ts b/desktop/tests/e2e/agent-provider-dropdowns.spec.ts
index 09fde8b8f9..de4ba9468b 100644
--- a/desktop/tests/e2e/agent-provider-dropdowns.spec.ts
+++ b/desktop/tests/e2e/agent-provider-dropdowns.spec.ts
@@ -169,6 +169,8 @@ test.describe("agent provider dropdown screenshots", () => {
       timeout: 8_000,
     });
 
+    await dialog.getByRole("tab", { name: "Customize for this agent" }).click();
+
     // Regression: the model combobox must appear (modelFieldVisible = true once
     // runtime is non-empty) and model discovery must have run, populating it.
     const modelCombobox = dialog.getByRole("combobox", { name: /model/i });
diff --git a/desktop/tests/e2e/agent-readiness-screenshots.spec.ts b/desktop/tests/e2e/agent-readiness-screenshots.spec.ts
index 20396cfb23..33d0d8f7aa 100644
--- a/desktop/tests/e2e/agent-readiness-screenshots.spec.ts
+++ b/desktop/tests/e2e/agent-readiness-screenshots.spec.ts
@@ -46,6 +46,7 @@ async function selectProvider(
   page: import("@playwright/test").Page,
   providerName: string,
 ) {
+  await page.getByRole("tab", { name: "Customize for this agent" }).click();
   await selectDropdownOption(
     page,
     page.locator("#persona-llm-provider"),
@@ -60,6 +61,15 @@ async function setCustomModel(
   page: import("@playwright/test").Page,
   modelId: string,
 ) {
+  await expect
+    .poll(() =>
+      page.evaluate(
+        () =>
+          (window as Window & { __BUZZ_E2E_COMMANDS__?: string[] })
+            .__BUZZ_E2E_COMMANDS__ ?? [],
+      ),
+    )
+    .toContain("discover_agent_models");
   await page.locator("#persona-model").click();
   await page
     .getByRole("button", { name: "Custom model...", exact: true })
@@ -119,44 +129,46 @@ test.describe("agent readiness gate screenshots", () => {
     });
   });
 
-  // Shot 01: buzz-agent selected, provider empty → required marker shown, save allowed.
-  test("01-create-buzzagent-empty-provider-marker", async ({ page }) => {
-    await installMockBridge(page);
-    await openCreateDialog(page);
-
-    // Wait for buzz-agent to auto-select and the provider field to render.
-    await expect(page.locator("#persona-llm-provider")).toBeVisible({
-      timeout: 10_000,
+  // Shot 01: inherited AI defaults are an explicit, valid choice. Provider and
+  // model controls stay hidden until the user chooses to customize this agent.
+  test("01-create-buzzagent-uses-ai-defaults", async ({ page }) => {
+    await installMockBridge(page, {
+      globalAgentConfig: {
+        provider: "anthropic",
+        model: "claude-opus-4-5",
+        env_vars: { ANTHROPIC_API_KEY: "sk-ant-test" },
+      },
     });
+    await openCreateDialog(page);
 
-    // Provider empty + no global provider → save is BLOCKED per the
-    // provider-default rule. The submit button must be disabled.
-    await expect(page.getByTestId("persona-dialog-submit")).toBeDisabled({
+    await expect(
+      page.getByRole("tab", { name: "Use AI defaults" }),
+    ).toHaveAttribute("data-state", "active");
+    await expect(page.locator("#persona-llm-provider")).not.toBeVisible();
+    await expect(page.locator("#persona-model")).not.toBeVisible();
+    await expect(page.getByTestId("persona-dialog-submit")).toBeEnabled({
       timeout: 10_000,
     });
     await settleAnimations(page);
 
-    const dialog = page.getByRole("dialog");
-    await dialog.screenshot({
-      path: `${SHOTS}/01-create-buzzagent-empty-provider-marker.png`,
+    await page.getByRole("dialog").screenshot({
+      path: `${SHOTS}/01-create-buzzagent-uses-ai-defaults.png`,
     });
   });
 
-  // Shot 02: buzz-agent + anthropic selected, model empty → required marker
-  // shown. The unified dialog blocks submit until the explicit model is set
-  // (anthropic requires one) — stricter than the legacy dialog's save-allowed.
-  test("02-create-buzzagent-empty-model-marker", async ({ page }) => {
+  // Shot 02: customized Anthropic configuration uses Automatic model instead
+  // of presenting an obsolete model-required error.
+  test("02-create-buzzagent-automatic-model", async ({ page }) => {
     await installMockBridge(page);
     await openCreateDialog(page);
-    await selectProvider(page, "Anthropic");
+    await selectProvider(page, "Buzz shared compute");
 
-    // Model still empty → required marker shown; submit blocked until set.
-    await expect(page.getByTestId("persona-dialog-submit")).toBeDisabled();
+    await expect(page.locator("#persona-model")).toContainText("Automatic");
+    await expect(page.getByTestId("persona-dialog-submit")).toBeEnabled();
     await settleAnimations(page);
 
-    const dialog = page.getByRole("dialog");
-    await dialog.screenshot({
-      path: `${SHOTS}/02-create-buzzagent-empty-model-marker.png`,
+    await page.getByRole("dialog").screenshot({
+      path: `${SHOTS}/02-create-buzzagent-automatic-model.png`,
     });
   });
 
@@ -252,11 +264,7 @@ test.describe("agent readiness gate screenshots", () => {
 
     await openCreateDialog(page);
 
-    // Wait for buzz-agent to auto-select (provider field visible), then
-    // switch to claude.
-    await expect(page.locator("#persona-llm-provider")).toBeVisible({
-      timeout: 10_000,
-    });
+    // Switch the auto-selected buzz-agent runtime to Claude Code.
     await selectDropdownOption(
       page,
       page.locator("#persona-runtime"),
@@ -305,8 +313,9 @@ test.describe("agent readiness gate screenshots", () => {
     await installMockBridge(page);
     await openCreateDialog(page);
 
-    // Buzz-agent auto-selects first; wait for its provider field, then
-    // switch to goose to confirm its required-marker behavior is identical.
+    // Opt into per-agent customization, then switch to goose to confirm a
+    // genuinely incomplete customized configuration remains blocked.
+    await page.getByRole("tab", { name: "Customize for this agent" }).click();
     await expect(page.locator("#persona-llm-provider")).toBeVisible({
       timeout: 10_000,
     });
diff --git a/desktop/tests/e2e/agents.spec.ts b/desktop/tests/e2e/agents.spec.ts
index c1972794fd..543dd830b8 100644
--- a/desktop/tests/e2e/agents.spec.ts
+++ b/desktop/tests/e2e/agents.spec.ts
@@ -246,10 +246,6 @@ test("built-in persona edits persist", async ({ page }) => {
   const dialog = page.getByTestId("persona-dialog");
   await dialog.getByLabel("Agent name").fill("My Fizz");
   await dialog.getByLabel("Agent instruction").fill("User-edited instructions");
-  await dialog.getByRole("button", { name: "LLM provider" }).click();
-  await page
-    .getByRole("menuitemradio", { name: "Anthropic", exact: true })
-    .click();
   await dialog.getByRole("button", { name: "Save changes" }).click();
 
   await expect(dialog).toHaveCount(0);
diff --git a/desktop/tests/e2e/persona-env-vars.spec.ts b/desktop/tests/e2e/persona-env-vars.spec.ts
index 4ffc5cc8b5..b9b8281424 100644
--- a/desktop/tests/e2e/persona-env-vars.spec.ts
+++ b/desktop/tests/e2e/persona-env-vars.spec.ts
@@ -318,13 +318,14 @@ test("persona model options follow the selected LLM provider", async ({
   await page.getByRole("menuitem", { name: /^New agent$/ }).click();
 
   const provider = page.locator("#persona-runtime");
+  await page.getByRole("tab", { name: "Customize for this agent" }).click();
   const llmProvider = page.locator("#persona-llm-provider");
   const model = page.locator("#persona-model");
   await expect(provider).toContainText("Buzz Agent (default)");
   await expect(llmProvider).toBeVisible();
   await expect(model).toBeVisible();
-  // Without live discovery, the only static option is "Default model".
-  await expect(model).toContainText("Default model");
+  // Custom mode requires a model selection until a provider is chosen.
+  await expect(model).toContainText("Choose a model");
 
   await selectDropdownOption(page, llmProvider, "OpenAI");
   const dialog = page.getByRole("dialog");
@@ -345,11 +346,10 @@ test("persona model options follow the selected LLM provider", async ({
   await expect(dialog.getByLabel("OpenAI API Key")).not.toBeVisible();
   await expect(model).toBeVisible();
 
-  // Switch to Default (no explicit provider) — required rows disappear,
-  // model resets to "Default model".
-  // Provider blank option was renamed from "Default" to "Select a provider\u2026"
-  // by the global-provider label change in this branch (getDefaultLlmProviderLabel).
-  await selectDropdownOption(page, llmProvider, "Select a provider\u2026");
-  await expect(model).toBeVisible();
-  await expect(model).toContainText("Default model");
+  // Switch back to inherited defaults — per-agent provider, credential, and
+  // model controls disappear together.
+  await page.getByRole("tab", { name: "Use AI defaults" }).click();
+  await expect(llmProvider).not.toBeVisible();
+  await expect(dialog.getByLabel("Anthropic API Key")).not.toBeVisible();
+  await expect(model).not.toBeVisible();
 });
