From b8178d5cba0e3894605ed4872e74883f568bf399 Mon Sep 17 00:00:00 2001 From: kenny lopez Date: Mon, 27 Jul 2026 13:33:11 +0100 Subject: [PATCH] Preserve harness merge resolutions after DCO rebase Signed-off-by: kenny lopez --- desktop/src-tauri/src/commands/agents.rs | 12 +++--- .../src-tauri/src/commands/agents_tests.rs | 6 +-- .../src-tauri/src/managed_agents/discovery.rs | 2 - .../src/managed_agents/discovery/tests.rs | 17 ++++---- .../managed_agents/effective_config/mod.rs | 16 ++++++-- .../managed_agents/effective_config/tests.rs | 3 +- .../src/managed_agents/global_config/mod.rs | 33 ++++++---------- .../src/managed_agents/global_config/tests.rs | 39 +++++++++++++++---- desktop/src-tauri/src/managed_agents/mod.rs | 5 +-- .../managed_agents/persona_events/tests.rs | 1 + .../src-tauri/src/managed_agents/readiness.rs | 3 +- .../src/managed_agents/spawn_hash/tests.rs | 1 + .../agents/ui/AgentDefinitionDialog.tsx | 38 ++---------------- .../agents/ui/AgentDefinitionDialog.types.ts | 27 +++++++++++++ .../agents/ui/AgentInstanceEditDialog.tsx | 20 ++-------- .../ui/AgentInstanceEditDialog.types.ts | 14 +++++++ .../agents/ui/agentAdvancedFieldsMotion.ts | 4 ++ .../onboarding/welcomeKickoff.test.mjs | 28 +++++++++++++ desktop/src/shared/api/types.ts | 13 ++----- desktop/tests/e2e/onboarding.spec.ts | 2 +- 20 files changed, 164 insertions(+), 120 deletions(-) create mode 100644 desktop/src/features/agents/ui/AgentDefinitionDialog.types.ts create mode 100644 desktop/src/features/agents/ui/AgentInstanceEditDialog.types.ts create mode 100644 desktop/src/features/agents/ui/agentAdvancedFieldsMotion.ts diff --git a/desktop/src-tauri/src/commands/agents.rs b/desktop/src-tauri/src/commands/agents.rs index 826d86882..0ef65f129 100644 --- a/desktop/src-tauri/src/commands/agents.rs +++ b/desktop/src-tauri/src/commands/agents.rs @@ -759,13 +759,11 @@ pub async fn create_managed_agent( // Load personas once for harness/pack/avatar resolution below. let personas = load_personas(&app).unwrap_or_default(); - // The persona runtime is authoritative. Persist an override only when - // the selected runtime must remain pinned (`harness_override`). This - // includes explicit picks and the visible fallback for a runtime-less - // persona. `implicit_harness_fallback` distinguishes those intents so - // the pin cannot adopt defaults owned by another harness. A divergence - // without `harness_override` remains inherited so it can recover when - // its configured runtime is installed. Persona-less creates always pin. + // The persona runtime is authoritative. Persist an override only for a + // pinned selection, including the visible runtime-less fallback. + // `implicit_harness_fallback` distinguishes that automatic fallback so + // it cannot adopt defaults owned by another harness. Other divergence + // stays inherited; persona-less creates always pin. let (agent_command_override, agent_command_override_is_implicit) = crate::managed_agents::create_time_agent_command_override_state( requested_persona_id.as_deref(), diff --git a/desktop/src-tauri/src/commands/agents_tests.rs b/desktop/src-tauri/src/commands/agents_tests.rs index 63a5ab3eb..101ba7080 100644 --- a/desktop/src-tauri/src/commands/agents_tests.rs +++ b/desktop/src-tauri/src/commands/agents_tests.rs @@ -17,7 +17,7 @@ fn bare_agent_record( relay_url: "ws://localhost:3000".to_string(), avatar_url: None, acp_command: "buzz-acp".to_string(), - agent_command: "goose".to_string(), + agent_command: "buzz-agent".to_string(), agent_command_override: None, agent_command_override_is_implicit: false, agent_args: vec![], @@ -219,7 +219,7 @@ fn deploy_resolver_returns_none_for_orphaned_instance() { } #[test] -fn deploy_resolver_ignores_defaults_from_a_different_implicit_runtime() { +fn deploy_resolver_keeps_defaults_for_an_explicit_harness_override() { let mut record = bare_agent_record(Some("p1"), None, None); record.agent_command_override = Some("goose".to_string()); let personas = vec![persona_record("p1", None, None)]; @@ -232,7 +232,7 @@ fn deploy_resolver_ignores_defaults_from_a_different_implicit_runtime() { assert_eq!( resolve_deploy_model_provider(&record, &personas, &global), - (None, None) + (Some("auto".to_string()), Some("relay-mesh".to_string())) ); } diff --git a/desktop/src-tauri/src/managed_agents/discovery.rs b/desktop/src-tauri/src/managed_agents/discovery.rs index db9cd4ffd..35034ecea 100644 --- a/desktop/src-tauri/src/managed_agents/discovery.rs +++ b/desktop/src-tauri/src/managed_agents/discovery.rs @@ -362,8 +362,6 @@ pub fn effective_agent_command( } mod overrides; -#[cfg(test)] -pub use overrides::create_time_agent_command_override; pub use overrides::{apply_agent_command_update, create_time_agent_command_override_state}; /// Prefix of the typed dangling-harness error produced by diff --git a/desktop/src-tauri/src/managed_agents/discovery/tests.rs b/desktop/src-tauri/src/managed_agents/discovery/tests.rs index 7f1806a5c..bccabd25c 100644 --- a/desktop/src-tauri/src/managed_agents/discovery/tests.rs +++ b/desktop/src-tauri/src/managed_agents/discovery/tests.rs @@ -1,14 +1,17 @@ use std::path::PathBuf; -use super::overrides::{divergent_agent_command_override, update_time_agent_command_override}; +use super::overrides::{ + create_time_agent_command_override, divergent_agent_command_override, + update_time_agent_command_override, +}; use super::{ apply_agent_command_update, classify_runtime, codex_adapter_availability, - codex_adapter_is_outdated, create_time_agent_command_override, default_agent_command, - effective_agent_command, find_nvm_default_bin, find_via_login_shell, - is_login_shell_path_uninit, is_safe_nvm_tag, managed_agent_avatar_url, normalize_agent_args, - parse_semver_tag, preset_catalog_entry, probe_codex_acp_major_version, record_agent_command, - refresh_login_shell_path, try_record_agent_command, PresetHarness, BUZZ_AGENT_AVATAR_URL, - CLAUDE_CODE_AVATAR_URL, CODEX_AVATAR_URL, GOOSE_AVATAR_URL, + codex_adapter_is_outdated, default_agent_command, effective_agent_command, + find_nvm_default_bin, find_via_login_shell, is_login_shell_path_uninit, is_safe_nvm_tag, + managed_agent_avatar_url, normalize_agent_args, parse_semver_tag, preset_catalog_entry, + probe_codex_acp_major_version, record_agent_command, refresh_login_shell_path, + try_record_agent_command, PresetHarness, BUZZ_AGENT_AVATAR_URL, CLAUDE_CODE_AVATAR_URL, + CODEX_AVATAR_URL, GOOSE_AVATAR_URL, }; use crate::managed_agents::AcpAvailabilityStatus; diff --git a/desktop/src-tauri/src/managed_agents/effective_config/mod.rs b/desktop/src-tauri/src/managed_agents/effective_config/mod.rs index e079c76a1..8abc335b6 100644 --- a/desktop/src-tauri/src/managed_agents/effective_config/mod.rs +++ b/desktop/src-tauri/src/managed_agents/effective_config/mod.rs @@ -72,16 +72,22 @@ fn non_blank(v: Option<&str>) -> Option<&str> { } fn resolve_linked( + record: &ManagedAgentRecord, definition: &AgentDefinition, + definitions: &[AgentDefinition], global: &GlobalAgentConfig, ) -> EffectiveAgentConfig { + let global_defaults_apply = + super::global_config::global_harness_defaults_apply_to_record(record, definitions, global); let model = match non_blank(definition.model.as_deref()) { Some(m) => ResolvedField { value: Some(m.to_owned()), source: ConfigSource::Definition, }, None => ResolvedField { - value: global.model.clone(), + value: global_defaults_apply + .then(|| global.model.clone()) + .flatten(), source: ConfigSource::Global, }, }; @@ -92,7 +98,9 @@ fn resolve_linked( source: ConfigSource::Definition, }, None => ResolvedField { - value: global.provider.clone(), + value: global_defaults_apply + .then(|| global.provider.clone()) + .flatten(), source: ConfigSource::Global, }, }; @@ -251,7 +259,9 @@ pub fn resolve_effective_config( ) -> EffectiveConfigResult { match &record.persona_id { Some(pid) => match definitions.iter().find(|d| d.id == *pid) { - Some(def) => EffectiveConfigResult::Resolved(resolve_linked(def, global)), + Some(def) => { + EffectiveConfigResult::Resolved(resolve_linked(record, def, definitions, global)) + } None => EffectiveConfigResult::OrphanedInstance { record_pubkey: record.pubkey.clone(), missing_persona_id: pid.clone(), diff --git a/desktop/src-tauri/src/managed_agents/effective_config/tests.rs b/desktop/src-tauri/src/managed_agents/effective_config/tests.rs index 81c2611d5..4f7a11d3e 100644 --- a/desktop/src-tauri/src/managed_agents/effective_config/tests.rs +++ b/desktop/src-tauri/src/managed_agents/effective_config/tests.rs @@ -45,8 +45,9 @@ fn record( relay_url: "ws://localhost:3000".to_string(), avatar_url: None, acp_command: "buzz-acp".to_string(), - agent_command: "goose".to_string(), + agent_command: "buzz-agent".to_string(), agent_command_override: None, + agent_command_override_is_implicit: false, agent_args: vec![], mcp_command: "".to_string(), turn_timeout_seconds: 300, diff --git a/desktop/src-tauri/src/managed_agents/global_config/mod.rs b/desktop/src-tauri/src/managed_agents/global_config/mod.rs index ac3ff9429..b362ff505 100644 --- a/desktop/src-tauri/src/managed_agents/global_config/mod.rs +++ b/desktop/src-tauri/src/managed_agents/global_config/mod.rs @@ -223,7 +223,7 @@ pub fn save_global_agent_config(app: &AppHandle, config: &GlobalAgentConfig) -> /// harness. Explicitly configured definitions and standalone agents keep /// normal global inheritance. Configs written before `preferred_runtime` /// existed implicitly belong to Buzz Agent. -fn global_harness_defaults_apply_to_record( +pub(crate) fn global_harness_defaults_apply_to_record( record: &ManagedAgentRecord, personas: &[AgentDefinition], global: &GlobalAgentConfig, @@ -255,27 +255,18 @@ fn global_harness_defaults_apply_to_record( let preferred_runtime = global .preferred_runtime .as_deref() - .and_then(crate::managed_agents::known_acp_runtime) - .or_else(|| crate::managed_agents::known_acp_runtime("buzz-agent")); - let selected_command = crate::managed_agents::record_agent_command(record, personas); - let selected_runtime = crate::managed_agents::known_acp_runtime(&selected_command); + .filter(|runtime| !runtime.trim().is_empty()) + .unwrap_or("buzz-agent"); + let preferred_command = crate::managed_agents::known_acp_runtime_exact(preferred_runtime) + .and_then(|runtime| runtime.commands.first().copied()) + .map(str::to_string) + .or_else(|| { + crate::managed_agents::custom_harnesses::lookup_loaded_harness_by_id(preferred_runtime) + .map(|harness| harness.command.clone()) + }) + .unwrap_or_else(|| preferred_runtime.to_string()); - selected_runtime.is_some_and(|selected| { - preferred_runtime.is_some_and(|preferred| preferred.id == selected.id) - }) -} - -/// Return the global provider/model values that are safe for this record. -pub(crate) fn global_model_provider_for_record<'a>( - record: &ManagedAgentRecord, - personas: &[AgentDefinition], - global: &'a GlobalAgentConfig, -) -> (Option<&'a str>, Option<&'a str>) { - if global_harness_defaults_apply_to_record(record, personas, global) { - (global.model.as_deref(), global.provider.as_deref()) - } else { - (None, None) - } + record.agent_command.trim() == preferred_command.trim() } /// Return global env vars with harness-dependent defaults masked when an diff --git a/desktop/src-tauri/src/managed_agents/global_config/tests.rs b/desktop/src-tauri/src/managed_agents/global_config/tests.rs index 2436e9f55..01278b719 100644 --- a/desktop/src-tauri/src/managed_agents/global_config/tests.rs +++ b/desktop/src-tauri/src/managed_agents/global_config/tests.rs @@ -307,7 +307,7 @@ fn bare_record() -> ManagedAgentRecord { relay_url: "ws://localhost:3000".to_string(), avatar_url: None, acp_command: "buzz-acp".to_string(), - agent_command: "goose".to_string(), + agent_command: "buzz-agent".to_string(), agent_command_override: None, agent_command_override_is_implicit: false, agent_args: vec![], @@ -494,10 +494,6 @@ fn explicit_harness_override_keeps_defaults_from_a_different_preferred_runtime() global_env_vars_for_record(&record, &personas, &global), global.env_vars ); - assert_eq!( - global_env_vars_for_record(&record, &personas, &global), - global.env_vars - ); } #[test] @@ -547,6 +543,26 @@ fn explicit_harness_override_keeps_legacy_global_defaults() { ); } +#[test] +fn runtime_fallback_does_not_inherit_defaults_from_a_different_preferred_runtime() { + let mut record = bare_record(); + record.persona_id = Some("p1".to_string()); + record.agent_command = "goose".to_string(); + record.agent_command_override = None; + let personas = vec![persona("p1", None, None)]; + let global = GlobalAgentConfig { + model: Some("auto".to_string()), + provider: Some("relay-mesh".to_string()), + preferred_runtime: Some("buzz-agent".to_string()), + ..Default::default() + }; + + assert_eq!( + resolve_effective_model_provider(&record, &personas, &global), + (None, None) + ); +} + #[test] fn default_command_fallback_does_not_inherit_defaults_from_another_preferred_runtime() { let mut record = bare_record(); @@ -571,7 +587,8 @@ fn default_command_fallback_does_not_inherit_defaults_from_another_preferred_run fn runtime_fallback_inherits_defaults_when_it_matches_the_preferred_runtime() { let mut record = bare_record(); record.persona_id = Some("p1".to_string()); - record.agent_command_override = Some("goose".to_string()); + record.agent_command = "goose".to_string(); + record.agent_command_override = None; let personas = vec![persona("p1", None, None)]; let global = GlobalAgentConfig { model: Some("global-model".to_string()), @@ -582,7 +599,10 @@ fn runtime_fallback_inherits_defaults_when_it_matches_the_preferred_runtime() { assert_eq!( resolve_effective_model_provider(&record, &personas, &global), - (Some("global-model"), Some("global-provider")) + ( + Some("global-model".to_string()), + Some("global-provider".to_string()) + ) ); } @@ -601,7 +621,10 @@ fn explicit_runtime_keeps_global_defaults_when_another_runtime_is_preferred() { assert_eq!( resolve_effective_model_provider(&record, &personas, &global), - (Some("global-model"), Some("global-provider")) + ( + Some("global-model".to_string()), + Some("global-provider".to_string()) + ) ); } diff --git a/desktop/src-tauri/src/managed_agents/mod.rs b/desktop/src-tauri/src/managed_agents/mod.rs index 6dda8c6b5..0edb89c72 100644 --- a/desktop/src-tauri/src/managed_agents/mod.rs +++ b/desktop/src-tauri/src/managed_agents/mod.rs @@ -52,9 +52,8 @@ pub use env_vars::*; pub(crate) use git_bash::git_bash_available; pub(crate) use git_bash::{discover_git_bash, GitBashPrerequisite}; pub(crate) use global_config::{ - global_env_vars_for_record, global_model_provider_for_record, load_global_agent_config, - resolve_effective_model_provider, save_global_agent_config, validate_global_config, - GlobalAgentConfig, + global_env_vars_for_record, load_global_agent_config, resolve_effective_model_provider, + save_global_agent_config, validate_global_config, GlobalAgentConfig, }; pub(crate) use managed_node_paths::*; pub use nest::*; diff --git a/desktop/src-tauri/src/managed_agents/persona_events/tests.rs b/desktop/src-tauri/src/managed_agents/persona_events/tests.rs index 27d3b0ce0..9fe94b2f4 100644 --- a/desktop/src-tauri/src/managed_agents/persona_events/tests.rs +++ b/desktop/src-tauri/src/managed_agents/persona_events/tests.rs @@ -15,6 +15,7 @@ fn sample_record() -> ManagedAgentRecord { acp_command: "buzz-acp".into(), agent_command: "goose".into(), agent_command_override: None, + agent_command_override_is_implicit: false, agent_args: vec![], mcp_command: String::new(), turn_timeout_seconds: 320, diff --git a/desktop/src-tauri/src/managed_agents/readiness.rs b/desktop/src-tauri/src/managed_agents/readiness.rs index e2145caba..54a3cd543 100644 --- a/desktop/src-tauri/src/managed_agents/readiness.rs +++ b/desktop/src-tauri/src/managed_agents/readiness.rs @@ -252,8 +252,7 @@ fn resolve_effective_agent_env_with_def( } } - // Layer 3a: global env vars — the lowest user-settable layer. - // Injected before persona/agent so per-agent values win on collision. + // Layer 3a: global env vars, injected before persona/agent overrides. // `merged_user_env` with an empty "lower" map applies reserved/malformed-key // filtering to the global map for free. let applicable_global_env = diff --git a/desktop/src-tauri/src/managed_agents/spawn_hash/tests.rs b/desktop/src-tauri/src/managed_agents/spawn_hash/tests.rs index 3cc2fb89d..04c9c92b4 100644 --- a/desktop/src-tauri/src/managed_agents/spawn_hash/tests.rs +++ b/desktop/src-tauri/src/managed_agents/spawn_hash/tests.rs @@ -477,6 +477,7 @@ fn global_model_change_trips_hash_without_model_env_var() { let mut rec = record(); rec.persona_id = Some("p1".into()); rec.agent_command = "some-harness-without-model-env".into(); + rec.agent_command_override = Some(rec.agent_command.clone()); let personas = vec![{ let mut p = persona("p1", None, "prompt"); diff --git a/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx b/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx index 482bf5a3b..88495a5f9 100644 --- a/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx +++ b/desktop/src/features/agents/ui/AgentDefinitionDialog.tsx @@ -2,11 +2,6 @@ import * as React from "react"; import { ChevronDown } from "lucide-react"; import { AnimatePresence, motion, useReducedMotion } from "motion/react"; -import type { - AcpRuntimeCatalogEntry, - CreatePersonaInput, - UpdatePersonaInput, -} from "@/shared/api/types"; import { cn } from "@/shared/lib/cn"; import { Button } from "@/shared/ui/button"; import { ChooserDialogContent } from "@/shared/ui/chooser-dialog-content"; @@ -89,31 +84,8 @@ import { useSelectableAcpRuntimes, visibleAcpRuntimeSeedForCreate, } from "../lib/runtimeVisibilityPreference"; - -type AgentDefinitionDialogProps = { - open: boolean; - title: string; - description: string; - submitLabel: string; - initialValues: CreatePersonaInput | UpdatePersonaInput | null; - error: Error | null; - isPending: boolean; - runtimes: AcpRuntimeCatalogEntry[]; - runtimesLoading?: boolean; - onOpenChange: (open: boolean) => void; - onSubmit: ( - input: CreatePersonaInput | UpdatePersonaInput, - ) => Promise; - /** Rendered below the form fields in create mode only ("Where to run"). */ - createRunSection?: React.ReactNode; - /** Extra create-mode submit gate (e.g. incomplete provider config). */ - createSubmitBlocked?: boolean; -}; - -const ADVANCED_FIELDS_MOTION_TRANSITION = { - duration: 0.18, - ease: [0.23, 1, 0.32, 1], -} as const; +import type { AgentDefinitionDialogProps } from "./AgentDefinitionDialog.types"; +import { ADVANCED_FIELDS_MOTION_TRANSITION } from "./agentAdvancedFieldsMotion"; export function AgentDefinitionDialog({ open, @@ -400,11 +372,7 @@ export function AgentDefinitionDialog({ (runtime.trim().length > 0 && runtimeCanChooseLlmProvider) || blankRuntimeModelProviderEditable; const trimmedProvider = provider.trim(); - // Required credential env keys for this runtime + provider combination. - // Used to show required markers on the LLM provider label and amber - // locked rows in the env vars editor. - // File-layer config for the selected runtime (e.g. goose config.yaml). - // Used to silence requirements already satisfied there. + // File config satisfies credentials before the readiness gate renders them. const { data: runtimeFileConfig } = useRuntimeFileConfigQuery(runtime, { enabled: open, }); diff --git a/desktop/src/features/agents/ui/AgentDefinitionDialog.types.ts b/desktop/src/features/agents/ui/AgentDefinitionDialog.types.ts new file mode 100644 index 000000000..99a211d31 --- /dev/null +++ b/desktop/src/features/agents/ui/AgentDefinitionDialog.types.ts @@ -0,0 +1,27 @@ +import type { ReactNode } from "react"; + +import type { + AcpRuntimeCatalogEntry, + CreatePersonaInput, + UpdatePersonaInput, +} from "@/shared/api/types"; + +export type AgentDefinitionDialogProps = { + open: boolean; + title: string; + description: string; + submitLabel: string; + initialValues: CreatePersonaInput | UpdatePersonaInput | null; + error: Error | null; + isPending: boolean; + runtimes: AcpRuntimeCatalogEntry[]; + runtimesLoading?: boolean; + onOpenChange: (open: boolean) => void; + onSubmit: ( + input: CreatePersonaInput | UpdatePersonaInput, + ) => Promise; + /** Rendered below the form fields in create mode only ("Where to run"). */ + createRunSection?: ReactNode; + /** Extra create-mode submit gate (e.g. incomplete provider config). */ + createSubmitBlocked?: boolean; +}; diff --git a/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx b/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx index b523a9d8b..998433ef1 100644 --- a/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx +++ b/desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx @@ -13,11 +13,9 @@ import { } from "@/features/agents/hooks"; import { isManagedAgentActive } from "@/features/agents/lib/managedAgentControlActions"; import type { - ManagedAgent, RespondToMode, UpdateManagedAgentInput, } from "@/shared/api/types"; -import type { EditAgentFocusTarget } from "@/features/agents/openEditAgentEvent"; import { cn } from "@/shared/lib/cn"; import { Button } from "@/shared/ui/button"; import { ChooserDialogContent } from "@/shared/ui/chooser-dialog-content"; @@ -85,13 +83,10 @@ import { useProviderApiKeyFieldState } from "./providerApiKeyFieldState"; import { useSelectableAcpRuntimes } from "../lib/runtimeVisibilityPreference"; import { resolveModelFieldStatusMessage } from "./agentConfigControls"; import { AdvancedRequiredBadge } from "./AdvancedRequiredBadge"; +import { ADVANCED_FIELDS_MOTION_TRANSITION } from "./agentAdvancedFieldsMotion"; +import type { AgentInstanceEditDialogProps } from "./AgentInstanceEditDialog.types"; import { showAgentProfileSyncWarning } from "./agentProfileSyncWarning"; -const ADVANCED_FIELDS_MOTION_TRANSITION = { - duration: 0.18, - ease: [0.23, 1, 0.32, 1], -} as const; - export function AgentInstanceEditDialog({ agent, initialFocus, @@ -99,16 +94,7 @@ export function AgentInstanceEditDialog({ onEditLinkedPersona, onOpenChange, onUpdated, -}: { - agent: ManagedAgent; - /** Optional field to scroll/focus when the dialog opens from a card deep-link. */ - initialFocus?: EditAgentFocusTarget; - open: boolean; - /** Present only when the linked definition is editable (non-built-in, resolved). Caller closes this dialog and enters definition-edit. */ - onEditLinkedPersona?: () => void; - onOpenChange: (open: boolean) => void; - onUpdated?: (agent: ManagedAgent) => void; -}) { +}: AgentInstanceEditDialogProps) { const updateMutation = useUpdateManagedAgentMutation(); const startMutation = useStartManagedAgentMutation(); const runtimesQuery = useAcpRuntimesQuery({ enabled: open }); diff --git a/desktop/src/features/agents/ui/AgentInstanceEditDialog.types.ts b/desktop/src/features/agents/ui/AgentInstanceEditDialog.types.ts new file mode 100644 index 000000000..c7c16331c --- /dev/null +++ b/desktop/src/features/agents/ui/AgentInstanceEditDialog.types.ts @@ -0,0 +1,14 @@ +import type { EditAgentFocusTarget } from "@/features/agents/openEditAgentEvent"; +import type { ManagedAgent } from "@/shared/api/types"; + +export type AgentInstanceEditDialogProps = { + agent: ManagedAgent; + /** Optional field to scroll/focus when the dialog opens from a card deep-link. */ + initialFocus?: EditAgentFocusTarget; + open: boolean; + /** Present only when the linked definition is editable (non-built-in, + * resolved). Caller closes this dialog and enters definition-edit. */ + onEditLinkedPersona?: () => void; + onOpenChange: (open: boolean) => void; + onUpdated?: (agent: ManagedAgent) => void; +}; diff --git a/desktop/src/features/agents/ui/agentAdvancedFieldsMotion.ts b/desktop/src/features/agents/ui/agentAdvancedFieldsMotion.ts new file mode 100644 index 000000000..0a39b11fd --- /dev/null +++ b/desktop/src/features/agents/ui/agentAdvancedFieldsMotion.ts @@ -0,0 +1,4 @@ +export const ADVANCED_FIELDS_MOTION_TRANSITION = { + duration: 0.18, + ease: [0.23, 1, 0.32, 1], +} as const; diff --git a/desktop/src/features/onboarding/welcomeKickoff.test.mjs b/desktop/src/features/onboarding/welcomeKickoff.test.mjs index c21641b12..dd078587f 100644 --- a/desktop/src/features/onboarding/welcomeKickoff.test.mjs +++ b/desktop/src/features/onboarding/welcomeKickoff.test.mjs @@ -91,6 +91,34 @@ test("welcome readiness evaluates the runtime selected by the visible fallback", ); }); +test("welcome readiness keeps legacy Buzz Agent provider defaults", () => { + const runtimes = [ + { + id: "goose", + label: "Goose", + availability: "available", + authStatus: { status: "not_applicable" }, + }, + { + id: "buzz-agent", + label: "Buzz Agent", + availability: "available", + authStatus: { status: "not_applicable" }, + }, + ]; + const globalConfig = { + env_vars: { OPENAI_COMPAT_API_KEY: "e2e-placeholder" }, + provider: "openai", + model: "gpt-5.5", + preferred_runtime: null, + }; + + assert.deepEqual(resolveWelcomeAgentReadiness(runtimes, globalConfig, []), { + ready: true, + reason: "buzz-agent", + }); +}); + test("welcome provisioning requires an enabled available runtime", () => { const runtimes = [ { diff --git a/desktop/src/shared/api/types.ts b/desktop/src/shared/api/types.ts index d221c5c24..28eb2786e 100644 --- a/desktop/src/shared/api/types.ts +++ b/desktop/src/shared/api/types.ts @@ -449,17 +449,10 @@ export type CreateManagedAgentInput = { relayUrl?: string; acpCommand?: string; agentCommand?: string; - /** - * True when `agentCommand` is a runtime command the caller deliberately wants - * to preserve instead of inheriting the linked persona command. This covers - * deploy-dialog runtime selections and discovered or installed aliases for the - * same persona runtime id, while still ignoring missing-runtime fallbacks. - */ + /** True when `agentCommand` must remain pinned instead of inheriting the persona. + * Covers explicit choices and installed aliases, but not missing-runtime fallbacks. */ harnessOverride?: boolean; - /** - * True when `harnessOverride` pins an automatically selected runtime for a - * runtime-less persona rather than a runtime the user explicitly selected. - */ + /** Whether the pinned runtime is an automatic fallback for a runtime-less persona. */ implicitHarnessFallback?: boolean; agentArgs?: string[]; mcpCommand?: string; diff --git a/desktop/tests/e2e/onboarding.spec.ts b/desktop/tests/e2e/onboarding.spec.ts index 26e9faac9..a405ce41d 100644 --- a/desktop/tests/e2e/onboarding.spec.ts +++ b/desktop/tests/e2e/onboarding.spec.ts @@ -2681,7 +2681,7 @@ test("first-run onboarding posts the live Fizz kickoff", async ({ page }) => { page, { globalAgentConfig: { - env_vars: { OPENAI_API_KEY: "e2e-placeholder" }, + env_vars: { OPENAI_COMPAT_API_KEY: "e2e-placeholder" }, provider: "openai", model: "gpt-5.5", },