feat(desktop): global agent config defaults with readiness/spawn parity#1448
feat(desktop): global agent config defaults with readiness/spawn parity#1448wpfleger96 wants to merge 40 commits into
Conversation
34f9571 to
3903f9c
Compare
828f73d to
05d2467
Compare
c7b3b9d to
bb71500
Compare
de99aa5 to
c65a9ee
Compare
bb71500 to
04d8da2
Compare
c65a9ee to
820461d
Compare
e0a631a to
107f561
Compare
820461d to
691fce3
Compare
7ef6834 to
4f2f7b2
Compare
691fce3 to
6af58ae
Compare
607c5fd to
7e1a77d
Compare
f623802 to
dbf60e1
Compare
2eac66a to
2353a44
Compare
Agent defaults card — global provider, model, and env varGlobal configuration layer (#1448): Create Agent — required key driven by global provider (Test 2.1 + 2.2 fix)Global provider = Anthropic, no per-agent provider set → Create Agent — global env satisfies required key, Advanced collapsed (Test 4 nuance fix)Global Create gate BLOCKED — no provider, no global defaultsNo per-agent provider and no global provider set → Create gate ENABLED — global provider resolves the defaultGlobal provider = Anthropic → |
9bae17f to
1d399c7
Compare
…and on baked-keys settle Three bugs introduced in the initial baked-env PR: 1. PersonaDialog.tsx used allowlist semantics for requiredEnvKeys (include if in missingEnvKeys OR fileSatisfiedEnvKeys), which caused file-satisfied keys to render twice in EnvVarsEditor — once as an amber locked row and once as an info row — and caused filled required keys to drop out of the amber locked row on first character typed (focus loss). Fixed to exclusion semantics matching the other consumers: all required keys except baked-satisfied and file-satisfied ones. 2. useRequiredCredentialState auto-expanded the Advanced section before the baked-keys query settled, causing a first-open flicker: bakedEnvKeys was undefined, baked-covered keys transiently appeared missing, Advanced expanded, then the requirement disappeared when the query resolved. Fixed by gating the auto-expand on isFetched from the query. 3. useBakedBuildEnvKeysQuery lacked retry: false, causing 3 silent retries in web/E2E contexts where the Tauri command doesn't exist. Also extends getBakedSatisfiedEnvKeys JSDoc with the UX asymmetry rationale and the #1448 precedence insertion point note, and adds the missing "Queued to split." trailer to the tauri.ts file-size override comment.
1d399c7 to
8b127a7
Compare
PersonaDialog has two owners of the provider API key: - PersonaProviderApiKeyField above Advanced (the working input) - A locked amber Required row inside EnvVarsEditor via PersonaAdvancedFields getAdvancedEnvVars strips the managed key from the envVars value passed to the editor, but requiredEnvKeys still included it. The editor rendered a locked row whose value was always empty string; every keystroke was discarded by the handleAdvancedEnvVarsChange guard. The input appeared dead. Filter the managed key (providerApiKeyConfig?.envVar) out of requiredEnvKeys at the PersonaAdvancedFields call site. The dedicated field above remains the single owner of that credential slot. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
When a user selects a runtime provider in CreateAgentDialog that requires a credential (e.g. Anthropic needing ANTHROPIC_API_KEY), the required amber rows appear in the env-vars section below Advanced — but if Advanced is collapsed the user sees no visual signal that action is needed. Add an effect that opens Advanced the first time requiredEnvKeys becomes non-empty during a dialog-open cycle. A ref guards against re-opening after the user manually collapses the section. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Previously GlobalAgentConfigSettingsCard loaded its own useState copy via a separate getGlobalAgentConfig() call and only called invalidateQueries after save. invalidateQueries is a no-op when no Create/Edit dialog has ever mounted (no cached query entry exists yet), so the first dialog open still started from placeholderData: EMPTY_CONFIG and saw an async fetch race before requiredEnvKeys could be computed. Seed the TanStack Query cache via setQueryData in both paths: - on load success: any dialog that opens after the settings card mounts reads the populated value synchronously on first render - after save: the canonical saved config propagates synchronously to all open dialogs and any that open afterward Drop the invalidateQueries call — setQueryData is both sufficient and more correct (synchronous, no refetch round-trip needed). Fixes Test 2.1 (Create Agent missing required row), Test 4 nuance (Edit Agent showing stale global), and unblocks Test 3.3 deep-link focus (required row now present on first render so its ref registers before the focus dispatch fires). Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Fix 3 auto-opens the Advanced section the moment required env keys appear. The parallelism smoke test picked provider=anthropic (triggering auto-open), then clicked the 'Advanced setup' header unconditionally — which toggled the already-open section closed. The parallelism input inside was then hidden and the fill timed out. Guard the click on aria-expanded: only open if currently collapsed. Add a toBeVisible assertion before the fill so future regressions surface a clear failure message rather than a fill timeout. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…t dialogs (#1534) Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
… label/gate PersonaDialog was global-blind: computeLocalModeGate received no globalProvider/globalEnvVars/globalModel, so persona-bound agents never showed required credential rows when a global provider was set without a per-agent override (same root as Test 2.1/Test 4 failures in non-persona paths before the arc fixes). Gap A: import useGlobalAgentConfig into PersonaDialog, wrap computeLocalModeGate in useMemo with global args, replace the standalone requiredCredentialEnvKeys() call with the gate's own requiredEnvKeys (gate already filters globally- and file-satisfied keys). Gap B: add hasAutoOpenedAdvancedRef auto-expand effect (mirrors CreateAgentDialog) — fires once per dialog-open cycle when requiredEnvKeys is non-empty so the user sees the required credential row without manually expanding Advanced. Fix (a): update getDefaultLlmProviderLabel to accept globalProvider and return 'Inherit (<provider>)' when set or 'Select a provider…' when not. Thread through getPersonaProviderOptions and AgentProviderField. Add effective-provider save gate (effectiveProvider = per-agent || global) to canSubmit in CreateAgentDialog, EditAgentDialog, and PersonaDialog — save is only blocked when there is genuinely no provider to fall back to. Bump PersonaDialog file-size override from 1034 to 1077 (+43 lines for the global wiring and auto-expand effect). Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…-default rule Shot 01 (buzz-agent empty provider) and Shot 08 (goose empty provider) previously asserted toBeEnabled — the correct behavior pre-provider-default rule. Fix (a) now blocks save when no effective provider exists (per-agent OR global), so the assertions flip to toBeDisabled. Updated comments explain the new invariant. Add get_global_agent_config mock to e2eBridge.ts (returns configurable empty config instead of throwing 'Unsupported command'). Wire globalAgentConfig option through E2eConfig.mock and MockBridgeOptions so future tests can exercise Inherit-from-global behavior without a real backend. Add 9 unit tests to createAgentLocalModeGate.test.mjs covering: - getDefaultLlmProviderLabel: no/empty/set/whitespace global provider - effective-provider save gate: empty+no-global=blocked, empty+global=allowed, explicit=allowed - computeLocalModeGate global-aware required key: anthropic key appears when global provider=anthropic and key missing; absent when satisfied globally Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…del/provider fields
AgentModelField hardcoded id="agent-model" on both its label and select.
GlobalAgentConfigSettingsCard renders AgentModelField alongside CreateAgentDialog
on the same page — once the get_global_agent_config mock was fixed to resolve
instead of throw, the settings card rendered its AgentModelField and the DOM
had two elements with id="agent-model", causing strict-mode violations in any
page-wide locator('#agent-model') call.
Add an optional id prop to AgentModelField (defaulting to "agent-model" so
all existing callers are unaffected), and pass id="global-agent-model" at
the GlobalAgentConfigSettingsCard call site. The settings card's inline
provider field already used id="global-agent-provider" — this brings the
model field into parity and eliminates duplicate DOM ids entirely.
openEditAgentEvent.ts deep-link focus targets reference "agent-model" and
"agent-provider" for the Edit Agent dialog fields specifically — those paths
are unaffected since the dialogs continue to use the default id.
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…anges
Two spec updates reflecting intentional new behavior introduced by this branch:
1. Scope env-vars-editor queries to the dialog: GlobalAgentConfigSettingsCard
(new in this branch) renders its own EnvVarsEditor in the background settings
pane, so page-wide getByTestId('env-vars-editor') queries now see count >= 1
even before the dialog's Advanced section is opened. Scoped all env-vars-*
testid queries in both affected tests to page.getByRole('dialog').
2. Update post-OpenAI env-vars-editor assertion: the branch added auto-expand of
Advanced when required env keys are present. Selecting OpenAI surfaces
OPENAI_COMPAT_API_KEY as required and auto-opens Advanced — so the dialog's
env-vars-editor is now visible (count=1) immediately after switching provider.
Changed toHaveCount(0) to toHaveCount(1) + toBeVisible() to match.
3. Update 'Default' provider option name: getDefaultLlmProviderLabel was renamed
from returning 'Default' to 'Select a provider...' (with Unicode ellipsis) when
no global provider is set. Updated the selectDropdownOption call to match.
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…e additions Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…guard Post-#1540 the provider/model fields in EditAgentDialog render as PersonaDropdownField (a <button> via DropdownMenuTrigger), not native <select> elements. The deep-link focus effect had two compounding bugs: 1. Target IDs "agent-provider" / "agent-model" did not match the Edit JSX, which renders id="edit-agent-llm-provider" and id="edit-agent-model". getElementById returned null, silently skipping focus. 2. Guard "instanceof HTMLSelectElement" always bailed for a <button>, so focus never fired even if the IDs had been correct. Fix: update targetIds to match the rendered Edit-specific IDs; relax the guard to instanceof HTMLElement (focus + scrollIntoView both exist on HTMLElement). All other parts of the effect (llmProviderFieldVisible bail, rAF scroll/focus, one-shot ref) are unchanged. Add editAgentNormalizedFieldFocus.test.mjs (7 tests) covering provider and model field focus dispatch, legacy-ID regression guard, lazy provider-field visibility, one-shot guard, env_key passthrough, and closed-dialog no-op. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Add a read-only "Baked build defaults" section to GlobalAgentConfigSettingsCard
that shows the compile-time baked agent env (BUZZ_AGENT_PROVIDER, BUZZ_AGENT_MODEL,
and any other keys from baked_build_env()) with secret keys masked.
Backend (agent_config.rs):
- Add BakedEnvEntry struct { key, value, masked } (serde::Serialize)
- Add is_secret_key() heuristic: key contains _API_KEY, _TOKEN, _SECRET, _PASSWORD
(case-insensitive); non-secret values shown as-is
- Add get_baked_build_env() Tauri command: filters empty values, masks secrets
(replacement done in Rust so unmasked values never cross the IPC boundary),
returns Vec<BakedEnvEntry>
- Register command in lib.rs alongside get_baked_build_env_keys
- 8 unit tests: non-secret shown, api_key/token/secret/password masked, empty
filtered, mixed keys, case-insensitive heuristic
Frontend (tauri.ts):
- Add BakedEnvEntry type and getBakedBuildEnv() async binding
- Bump tauri.ts file-size override (1388 -> 1416)
Frontend (GlobalAgentConfigSettingsCard.tsx):
- Load baked env on mount via fire-and-forget effect (silent catch = OSS no-op)
- Render "Baked build defaults" section below env-vars editor, only when
bakedEnv.length > 0 (OSS builds return [] => zero visual change)
- Copy: "Set by your build. Override any of these above."
- Masked rows rendered at text-muted-foreground/50 to distinguish from real values
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…r rebase Semantic integration fixups after rebasing onto origin/main (48bd8ab): 1. spawn_hash.rs: spawn_config_hash now takes &GlobalAgentConfig so the restart-required hash covers global env changes (a global default change must trip the badge). Both call sites in runtime.rs updated: - build_managed_agent_summary: loads global config for the badge check - spawn_agent_child: reuses the already-loaded global at line 1525 2. Test struct completions: ManagedAgentRecord in agents_tests.rs and global_config/tests.rs was missing the fields added by Phase 1A.1 (display_name, slug, runtime, name_pool, is_builtin, is_active, source_team, source_team_persona_slug). Added with sensible defaults matching the existing spawn_hash/tests.rs pattern. 3. spawn_hash/tests.rs: all spawn_config_hash call sites updated to pass &Default::default() as the global arg (tests don't need global env). Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…-env
Three fixes from Thufir's re-review of the rebased tip:
1. global_agent_config: use record_agent_command instead of effective_agent_command
- Both call sites in collect_respawn_candidates and restart_setup_listener_agent
now use record_agent_command(record, &all_personas), which resolves via
record.runtime (override → record.runtime → persona fallback) rather than
the persona-first path that ignored ManagedAgentRecord.runtime.
- A global-config save that triggers a NotReady→Ready transition now evaluates
readiness against the runtime the agent actually spawns with.
- Adds regression test: record.runtime="claude" wins over persona.runtime="goose",
asserting the record-first command is used.
2. agent_config: invert baked-env masking to allowlist (default-deny)
- Replaces is_secret_key heuristic (contains _API_KEY/_TOKEN etc.) with
is_safe_to_reveal allowlist: BUZZ_AGENT_PROVIDER, BUZZ_AGENT_MODEL,
DATABRICKS_HOST, DATABRICKS_MODEL.
- Everything not in the allowlist is masked; bare names like APIKEY, TOKEN,
SECRET, PASSWORD, PRIVATE_KEY are now correctly masked where the old
heuristic would have leaked them.
- Updates and expands the 8 existing masking tests to cover bare-name cases.
3. GlobalAgentConfigSettingsCard: friendly labels for baked provider/model
- BUZZ_AGENT_PROVIDER renders as "Baked provider", BUZZ_AGENT_MODEL as
"Baked model"; all other keys render as raw key names unchanged.
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…-size override runtime.rs: wrap the merged_user_env call introduced in the #1639/1640 rebase conflict resolution — line exceeded rustfmt's limit. check-file-sizes.mjs: add override for AgentInstanceEditDialog.tsx (1043 lines). #1639 renamed EditAgentDialog to AgentInstanceEditDialog; #1448 rebase conflict resolution threaded initialFocus?/EditAgentFocusTarget through AgentDialog.tsx into AgentInstanceEditDialog.tsx, adding ~7 lines of feature plumbing. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…us prop #1448 extends AgentDialogInstanceEditProps with initialFocus to support deep-link focus in instance-edit mode. AgentDialog explicitly forwards initialFocus={props.initialFocus} to AgentInstanceEditDialog, so the contract test's deepEqual must include the prop (undefined when not passed by the caller). Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Previously providerValid blocked Save whenever the LLM-provider field was visible and no effective provider existed (neither per-agent nor global). This prevented renaming or editing timeout on any legacy goose agent that never had a provider configured — a regression from main's behavior where provider visibility was UX-only. Narrow to Will's (b): an agent that never had a provider stays fully editable. Only block when the user ACTIVELY clears a provider the agent originally had AND no global fallback covers it. Extract isEditAgentProviderSaveValid() as an exported pure helper so the three-way logic (field hidden / effective resolves / never had one) is testable in isolation. Add editAgentProviderSaveValid.test.mjs with 8 cases covering the no-provider agent, global-fallback, clear-existing, and whitespace-trimming paths. Bump AgentInstanceEditDialog.tsx file-size override 1043 → 1076 to account for the extracted helper function (feature logic, not debt). Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
The hadProvider=false exception in isEditAgentProviderSaveValid was too broad: it could not distinguish a legacy no-provider agent staying on its provider-capable runtime (safe name/timeout edits) from a user actively switching a non-provider-runtime agent INTO a provider-capable runtime without setting a provider — the latter would persist a provider-broken agent. Add originalRuntimeSupportsProvider to the helper's contract. Derive it at the call site from agent.agentCommand matched against the runtime catalog — a stable snapshot that does not mutate as the user edits the dropdown. The no-provider exception now only applies when the original runtime already supported provider selection (legacy state); switching INTO a provider-capable runtime from a non-provider one requires setting a provider. Update editAgentProviderSaveValid.test.mjs: add the new param to all existing cases, add two new cases for the runtime-switch scenario (no global → blocked; global covers → allowed). Bump AgentInstanceEditDialog.tsx file-size override 1076 → 1103. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Keep the provider save-gate's original runtime signal tied to the dialog-opening agent command while still resolving against the runtime catalog as it loads. This prevents prop refreshes or edit-state runtime ids from changing the legacy no-provider exception after the dialog is open. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Defect 1: useRequiredCredentialState used only the per-agent provider
when computing providerForRequiredKeys, with no global fallback. When a
global provider was set but the per-agent provider was empty, the hook
yielded no required keys — so ANTHROPIC_API_KEY never showed as amber in
Create/Edit Agent dialogs. Fix: add globalProvider param (optional,
defaults to ''), use (provider || globalProvider).trim() as the
effective provider. Caller (AgentInstanceEditDialog) already held
globalConfig — wired it in and moved the useGlobalAgentConfig call
above the hook invocation to satisfy forward-reference ordering.
Defect 2: the Advanced section auto-expanded only on requiredEnvKeyMissing
(missing→present transition). A fully-satisfied buzz-agent instance had
no missing key, so Advanced stayed collapsed and the model-tuning knobs
were unreachable without manual expand. Fix: add a parallel once-per-open
auto-expand effect in both CreateAgentDialog and AgentInstanceEditDialog
that fires whenever isBuzzAgentRuntime(prospectiveRuntimeId/selectedRuntimeId)
is true, independent of key-missing state.
Defect 3: provider and model inherit-option copy. getDefaultLlmProviderLabel
returned 'Inherit (<provider>)'; changed to 'Inherit global default (<provider>)'.
Added getDefaultLlmModelLabel with the same pattern ('Inherit global default (<model>)'
when global model is set, 'Default model' otherwise) and threaded it through
AgentModelField (CreateAgentDialog path) and the inline staticModelOptions
in AgentInstanceEditDialog. Updated provider-label tests and added
4 new model-label tests (2218 total, 0 fail).
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…Agent
The requiredEnvKeyMissing flag in useRequiredCredentialState was computed
using only per-agent envVars, ignoring globalConfig.env_vars. When a key
was satisfied exclusively at the global layer (e.g. ANTHROPIC_API_KEY set
in global config), the display filter in AgentInstanceEditDialog correctly
hid the amber row, but requiredEnvKeyMissing remained true and silently
disabled Save with no visible reason (the same Test-4 family defect, now
at the gate rather than the display).
Add globalEnvVars param (default {}) to useRequiredCredentialState. Wire
globalEnvVars: globalConfig.env_vars from AgentInstanceEditDialog. Exclude
globally-satisfied keys from requiredEnvKeys inside the hook itself so that
both the amber-row list and the save-gate marker are computed from a single
source of truth — display and gate can no longer drift.
Add editAgentRequiredCredentials.test.mjs with 4 cases covering the core
precedence: global+per-agent key -> not missing; global present, per-agent
empty -> not missing; global absent, per-agent filled -> not missing;
global absent, per-agent empty -> still missing (gate must block).
Also add buzz-agent model-tuning knobs (BuzzAgentModelTuningFields) to
the template dialogs (AgentDefinitionDialog / PersonaAdvancedFields).
Templates previously lacked the thinking/effort/max-rounds/max-output-
tokens controls available in agent instance dialogs. Add modelTuningRuntimeId
and inheritedEnvVars params to PersonaAdvancedFields; pass runtime and
globalConfig.env_vars from AgentDefinitionDialog. Add a once-per-open
auto-expand effect in AgentDefinitionDialog for isBuzzAgentRuntime(runtime)
so the tuning knobs are immediately reachable — mirrors the agent instance
path. The inherited baseline for template tuning placeholders is
globalConfig.env_vars (the layer templates fall through to).
Bump AgentInstanceEditDialog.tsx file-size override 1118 -> 1125.
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Three test struct literals missed the new auto_restart_on_config_change field added by the auto-restart PR (#1649) that landed on main before this rebase. Also wire State import in agent_settings.rs which the auto-restart command requires. This is a purely mechanical rebase-compatibility fix — no product logic change. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
… template dialogs AgentDefinitionDialog was computing model requiredness and model-option scoping from the local provider only, ignoring globalConfig.provider and the file-config fallback — the same split-source-of-truth class as F1 but on the model axis. Three concrete failures (Thufir pass-2): 1. local provider=anthropic, global model set, local model blank → template marked model required and disabled Save (wrong: gate satisfied by global model). 2. local provider blank, global provider=anthropic, global model blank → template allowed Save into a NotReady config (wrong). 3. global model set → template dropdown showed 'Default model' instead of 'Inherit global default (<model>)'. Fixes: - Add getDefaultLlmModelLabel to imports; use it for the zero-value model dropdown option so the inherit label is visible in template dialogs. - Remove providerForModelScope (local-only). Derive effectiveProvider = trimmedProvider || globalConfig.provider || fileProvider, mirroring the chain inside computeLocalModeGate, and use it for model-option scoping (usePersonaModelDiscovery, getPersonaModelOptions, shouldClearKnownModelForSelectionScope). - Replace isExplicitModelRequired with the effectiveProvider-based version (static asterisk on the model label now reflects the effective chain). - Replace the canSubmit model/provider guards with missingNormalizedFields.length === 0 — single source of truth with the readiness gate so display and Save can never drift again. - getPersonaProviderOptions first arg stays trimmedProvider (the local selection, needed to render the correct current-value custom option in the provider dropdown). - 3 new tests in createAgentLocalModeGate.test.mjs pinning the three failure cases above. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…r explicit-model providers
For providers that require an explicit model (e.g. anthropic),
getPersonaModelOptions filters out the zero-value option. The previous
inline map in AgentDefinitionDialog only relabelled an existing id=''
option, so for anthropic the inherit-global entry was never rendered.
Extract buildTemplateModelDropdownOptions to personaDialogPickers.tsx.
The helper guard-prepends { id: '', label: getDefaultLlmModelLabel(globalModel) }
when globalModel is non-empty AND no zero-value option already exists in
the discovered/static list. This makes the 'Inherit global default (<model>)'
entry visible and selectable for explicit-model providers when the global
model satisfies the requirement — without seeding an empty inherit option
when no global model is set (case 2 block preserved).
Three F3b acceptance tests added to createAgentLocalModeGate.test.mjs,
exercising the full option-list composition (not just getDefaultLlmModelLabel
in isolation): anthropic+global model set → inherit entry present; anthropic+no
global model → no zero-value entry; blank provider+global model → no double-seed.
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…#1633 #1633 added AcpAvailabilityStatus to the types import in readiness.rs alongside our GlobalAgentConfig import, bringing the merged line count to 1405 (gate) vs the prior 1403 cap. Ratchet to 1408. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…pi-key field Replace the two divergent provider->env-key mappings (requiredCredentialEnvKeys + getProviderApiKeyConfig) with a single PROVIDER_CREDENTIAL_CONFIG table in personaDialogPickers.tsx. Each entry carries requiredEnvKeys (keys the gate surfaces as amber rows) and an optional secretEnvVar (the typed secret cleared on provider switch). Databricks has no secretEnvVar — DATABRICKS_HOST is a URL cleared by OAuth PKCE, not a secret credential. Delete PersonaProviderApiKeyField.tsx and its filter at line 976-978 of AgentDefinitionDialog. The filter was the root cause of T2: it removed ANTHROPIC_API_KEY from the required-row list when the provider matched getProviderApiKeyConfig (anthropic/openai), while DATABRICKS_HOST survived because it had no entry there. With the filter gone, all providers produce amber required rows from the same gate — consistent with the instance dialogs. Remove envVarsMergingAdvancedEdit (only consumer was AgentDefinitionDialog; the dedicated field it protected is gone). Remove hasAdvancedEnvVars and getAdvancedEnvVars from personaDialogEnvVars.ts (same). PersonaAdvancedFields now receives envVars directly and setEnvVars as its onChange callback. Update persona-env-vars.spec.ts: - Test at :265: Advanced is now auto-expanded on buzz-agent open (T3 pass confirmed this is intended); remove the collapsed-on-open assertion and the click-to-expand step. - Test at :315: remove persona-provider-api-key testid assertions; verify OPENAI_COMPAT_API_KEY and ANTHROPIC_API_KEY as amber required rows in EnvVarsEditor instead. Add 3 regression tests in createAgentLocalModeGate.test.mjs covering: - explicit anthropic → ANTHROPIC_API_KEY in requiredCredentialEnvKeys - inherit→explicit anthropic switch preserves required row - databricks → secretEnvVar is null (no provider-switch clear) Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…eyConfig fn, envVarsWithProviderApiKey fn All three were left after the PROVIDER_CREDENTIAL_CONFIG unification in the previous commit. None had any production callers: - ProviderApiKeyConfig and getProviderApiKeyConfig were only referenced by each other (the type in the function signature). - envVarsWithProviderApiKey was only referenced by its own test file. Leaving them was the exact class of drift that caused the T2 defect: two independent provider→env-key mappings that could silently diverge. Remove all three to enforce the single source of truth. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Rebased onto origin/main (fc8a174) which introduced last_error_code on ManagedAgentRecord. Two test-only struct initializers used exhaustive init syntax and were missing the new field — cargo test failed to compile. Add last_error_code: None to each. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
014032c to
aefde06
Compare





Adds a global agent configuration layer that lets users set default provider, model, and environment variables applied to every agent unless overridden per-agent or per-template. Surfaces the baked build environment in the same card. Wires deep-link field focus from
ConfigNudgeCard. Adds a save-gate for the instance-edit dialog to guard against completing a runtime switch to a provider-capable runtime without a provider selected.Global config layer
GlobalAgentConfigstruct (provider,model,env_vars) persisted toglobal-agent-config.json.get_global_agent_config,set_global_agent_config(reserved and derived keys rejected byvalidate_global_config).GlobalAgentConfigSettingsCardrendered inAgentsView; model/provider fields, env-vars editor showing global defaults as inherited rows, and (Block builds only) a read-only "Baked build defaults" section below.useGlobalAgentConfigsingleton via TanStack Query (staleTime: Infinity, cache seeded on save).Readiness / spawn parity (Fix 1)
resolve_effective_model_provider(record, personas, global)inglobal_config/mod.rsencodes the agent → template → global → None precedence chain and is wired into bothresolve_effective_agent_env(readiness evaluation) andspawn_agent_child(runtime). Before this fix, readiness used the full fallback chain but spawn read onlyrecord.model/record.provider, so a global-default-only agent reported Ready but spawned without provider/model env.Override baseline for global-default agents (Fix 2)
In
resolve_config_surface, when!had_model && persona_model.is_none() && model_overridden, use(global.model, ConfigOrigin::GlobalDefault)as the baseline so the config surface UI shows the correct secondary row for a global-default-only agent.Required-row satisfaction (Fix 3)
isRequiredKeyMissinginEnvVarsEditor.tsxnow checksinheritedFrom?.[key]in addition to the agent-local value. Before this fix, a globally-satisfied required key still rendered the amber "Required" badge.Global config wired into dialogs
computeLocalModeGate(inPersonaDialogandAgentInstanceEditDialog) receivesglobalConfigand usesuseGlobalAgentConfigto evaluate readiness against the effective merged env — required markers, amber locked-required rows, and the auto-expand Advanced effect all work correctly against global defaults.AgentInstanceEditDialogexposes aproviderValidsave-gate: blocks saving when the agent has no per-agent or global provider but the selected runtime requires one. TheoriginalRuntimeSupportsProviderflag (snapshotted at dialog open fromagent.agentCommand, not the in-dialog selection) ensures the gate only fires on a switch INTO a provider-capable runtime, not for a legacy agent already on one.CreateAgentDialogandEditAgentAdvancedFieldssurface the global env as inherited rows with label "template / global defaults".Tier-1 buzz-agent model-tuning fields (#1534)
BuzzAgentModelTuningFields(temperature, top-p, max-tokens) wired intoEditAgentAdvancedFieldsfor thebuzz-agentruntime.Deep-link field focus from ConfigNudgeCard
ConfigNudgeCardcan fire anopenEditAgentevent with afocusTargetfield name.UserProfilePaneland the Edit Agent dialog readEditAgentFocusTargetto focus the correct field (provider, model, or an env-var key) when the dialog opens.Auto-respawn and setup-listener
respawn_newly_ready_agentsis called afterset_global_agent_config, so agents blocked only on a global default restart automatically once the default is saved. The setup-listener wiring routes auto-respawn through the normal spawn path.Baked build defaults
get_baked_build_env()Tauri command returns the compile-time baked agent env with secret keys masked in Rust (values for keys matching_API_KEY,_TOKEN,_SECRET,_PASSWORDreplaced with••••••before crossing the IPC boundary).GlobalAgentConfigSettingsCardrenders a read-only "Baked build defaults" section only when the list is non-empty; OSS builds return[]and the section is hidden.Related: block/buzz#1633