diff --git a/server/src/routes/agents.ts b/server/src/routes/agents.ts index 2ad85e63..c2cacfe4 100644 --- a/server/src/routes/agents.ts +++ b/server/src/routes/agents.ts @@ -1772,6 +1772,18 @@ export function agentRoutes(db: Db) { rawEffectiveAdapterConfig = { ...existingAdapterConfig, ...requestedAdapterConfig }; } if (changingAdapterType) { + // Preserve adapter-agnostic keys (env, cwd, etc.) from the existing config + // when the adapter type changes. Without this, a PATCH that includes + // adapterConfig but omits these keys would silently drop them. + const ADAPTER_AGNOSTIC_KEYS = [ + "env", "cwd", "timeoutSec", "graceSec", + "promptTemplate", "bootstrapPromptTemplate", + ] as const; + for (const key of ADAPTER_AGNOSTIC_KEYS) { + if (rawEffectiveAdapterConfig[key] === undefined && existingAdapterConfig[key] !== undefined) { + rawEffectiveAdapterConfig = { ...rawEffectiveAdapterConfig, [key]: existingAdapterConfig[key] }; + } + } rawEffectiveAdapterConfig = preserveInstructionsBundleConfig( existingAdapterConfig, rawEffectiveAdapterConfig, diff --git a/ui/src/components/AgentConfigForm.tsx b/ui/src/components/AgentConfigForm.tsx index 06c74e65..c3c9bdfa 100644 --- a/ui/src/components/AgentConfigForm.tsx +++ b/ui/src/components/AgentConfigForm.tsx @@ -1155,9 +1155,17 @@ function EnvVarEditor({ const [rows, setRows] = useState(() => toRows(value)); const [sealError, setSealError] = useState(null); const valueRef = useRef(value); + const emittingRef = useRef(false); - // Sync when value identity changes (overlay reset after save) + // Sync when value identity changes (overlay reset after save). + // Skip re-sync when the change was triggered by our own emit() to avoid + // reverting local row state (e.g. a secret-transition dropdown choice). useEffect(() => { + if (emittingRef.current) { + emittingRef.current = false; + valueRef.current = value; + return; + } if (value !== valueRef.current) { valueRef.current = value; setRows(toRows(value)); @@ -1170,12 +1178,18 @@ function EnvVarEditor({ const k = row.key.trim(); if (!k) continue; if (row.source === "secret") { - if (!row.secretId) continue; - rec[k] = { type: "secret_ref", secretId: row.secretId, version: "latest" }; + if (row.secretId) { + rec[k] = { type: "secret_ref", secretId: row.secretId, version: "latest" }; + } else { + // Row is transitioning to secret but user hasn't picked one yet. + // Preserve the plain value so it isn't silently dropped. + rec[k] = { type: "plain", value: row.plainValue }; + } } else { rec[k] = { type: "plain", value: row.plainValue }; } } + emittingRef.current = true; onChange(Object.keys(rec).length > 0 ? rec : undefined); }